Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@SpiffGreen
Last active December 12, 2022 13:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SpiffGreen/e163579bfe9ac1f391b2efce9e95c7c5 to your computer and use it in GitHub Desktop.
Save SpiffGreen/e163579bfe9ac1f391b2efce9e95c7c5 to your computer and use it in GitHub Desktop.
This is a naive text summarization algorithm in javascript
const nlp = require("compromise");
const stopwords = require("./stopwords.json");
const isStopword = (word) => !!stopwords[word.toLowerCase().replace(/[^a-z]/g, '')];
const content = `
This concept of authentication and authorization in expresssjs can be achieved easily using middleware functions. One might ask, "What is a middleware?". Well, a middleware is simply a function. You could think of it as a function that processes requests before the final handler for the matched route does its job and responds to the client who made the request.
A sample use case of a middleware in relation to this topic is having a function(middleware) to check headers, cookies, or body of a request for an authentication token which is verified by the server to authenticate the current user, i.e to know the current user who made the request.
If the server can't authenticate the user, the middleware can simply handle the request and send an authentication error back to the user, without the request getting to the final handler.
This same idea for authenticating users can be applied to check if a user is authorized to use a particular resource after authentication. Enough talks, now let's start coding and see this in action.
`;
function summarize(text) {
// document cleaning
let doc = nlp(text);
doc.normalize();
const cleanedText = doc.text(); // grab the transformed text.
// sentences tokenization
const sentence_tokens = nlp.tokenize(cleanedText).json().map(token => token.text.replace(".", ""));
const word_tokens = (sentence_tokens.map(sentence_token => sentence_token.split(" "))).flat();
// word scoring:
const word_frequencies = {};
for (let word of word_tokens) {
if (!isStopword(word)) {
word = word.trim();
if (!word_frequencies.hasOwnProperty(word)) {
word_frequencies[word] = 1;
} else {
word_frequencies[word] += 1;
}
}
}
// sentence scoring:
const max_frequency = Math.max(...Object.values(word_frequencies));
for(let word in word_frequencies) {
word_frequencies[word] = word_frequencies[word]/max_frequency;
}
const sentence_scores = {};
for(let sentence of sentence_tokens) {
for(let word of sentence.split(" ")) {
if(word_frequencies.hasOwnProperty(word)) {
if(!sentence_scores.hasOwnProperty(sentence)) {
sentence_scores[sentence] = word_frequencies[word];
} else {
sentence_scores[sentence] += word_frequencies[word];
}
}
}
}
// sentence scoring:
const select_length = parseInt((sentence_tokens.length * 0.6).toString());
// console.log(select_length)
const sortable = Object.entries(sentence_scores)
.sort(([,score1],[,score2]) => score1-score2)
.reduce((r, [key, value]) => ({ ...r, [key]: value }), {});
// console.log(sortable);
const summary = Object.keys(sortable).slice(0, select_length);
return summary.join(". ");
}
const summary = summarize(content);
console.log(summary);
{
"a": 1,
"about": 1,
"above": 1,
"across": 1,
"after": 1,
"afterwards": 1,
"again": 1,
"against": 1,
"all": 1,
"almost": 1,
"alone": 1,
"along": 1,
"already": 1,
"also": 1,
"although": 1,
"always": 1,
"am": 1,
"among": 1,
"amongst": 1,
"amoungst": 1,
"amount": 1,
"an": 1,
"and": 1,
"another": 1,
"any": 1,
"anyhow": 1,
"anyone": 1,
"anything": 1,
"anyway": 1,
"anywhere": 1,
"are": 1,
"around": 1,
"as": 1,
"at": 1,
"back": 1,
"be": 1,
"became": 1,
"because": 1,
"become": 1,
"becomes": 1,
"becoming": 1,
"been": 1,
"before": 1,
"beforehand": 1,
"behind": 1,
"being": 1,
"below": 1,
"beside": 1,
"besides": 1,
"between": 1,
"beyond": 1,
"bill": 1,
"both": 1,
"bottom": 1,
"but": 1,
"by": 1,
"call": 1,
"can": 1,
"cannot": 1,
"cant": 1,
"co": 1,
"computer": 1,
"con": 1,
"could": 1,
"couldnt": 1,
"cry": 1,
"de": 1,
"describe": 1,
"detail": 1,
"do": 1,
"done": 1,
"down": 1,
"due": 1,
"during": 1,
"each": 1,
"eg": 1,
"eight": 1,
"either": 1,
"eleven": 1,
"else": 1,
"elsewhere": 1,
"empty": 1,
"enough": 1,
"etc": 1,
"even": 1,
"ever": 1,
"every": 1,
"everyone": 1,
"everything": 1,
"everywhere": 1,
"except": 1,
"few": 1,
"fifteen": 1,
"fify": 1,
"fill": 1,
"find": 1,
"fire": 1,
"first": 1,
"five": 1,
"for": 1,
"former": 1,
"formerly": 1,
"forty": 1,
"found": 1,
"four": 1,
"from": 1,
"front": 1,
"full": 1,
"further": 1,
"get": 1,
"give": 1,
"go": 1,
"had": 1,
"has": 1,
"hasnt": 1,
"have": 1,
"he": 1,
"hence": 1,
"her": 1,
"here": 1,
"hereafter": 1,
"hereby": 1,
"herein": 1,
"hereupon": 1,
"hers": 1,
"herse": 1,
"him": 1,
"himse": 1,
"his": 1,
"how": 1,
"however": 1,
"hundred": 1,
"i": 1,
"ie": 1,
"if": 1,
"in": 1,
"inc": 1,
"indeed": 1,
"interest": 1,
"into": 1,
"is": 1,
"it": 1,
"its": 1,
"itse": 1,
"keep": 1,
"last": 1,
"latter": 1,
"latterly": 1,
"least": 1,
"less": 1,
"ltd": 1,
"made": 1,
"many": 1,
"may": 1,
"me": 1,
"meanwhile": 1,
"might": 1,
"mill": 1,
"mine": 1,
"more": 1,
"moreover": 1,
"most": 1,
"mostly": 1,
"move": 1,
"much": 1,
"must": 1,
"my": 1,
"myse": 1,
"name": 1,
"namely": 1,
"neither": 1,
"never": 1,
"nevertheless": 1,
"next": 1,
"nine": 1,
"no": 1,
"nobody": 1,
"none": 1,
"noone": 1,
"nor": 1,
"not": 1,
"nothing": 1,
"now": 1,
"nowhere": 1,
"of": 1,
"off": 1,
"often": 1,
"on": 1,
"once": 1,
"one": 1,
"only": 1,
"onto": 1,
"or": 1,
"other": 1,
"others": 1,
"otherwise": 1,
"our": 1,
"ours": 1,
"ourselves": 1,
"out": 1,
"over": 1,
"own": 1,
"part": 1,
"per": 1,
"perhaps": 1,
"please": 1,
"put": 1,
"rather": 1,
"re": 1,
"rt": 1,
"same": 1,
"see": 1,
"seem": 1,
"seemed": 1,
"seeming": 1,
"seems": 1,
"serious": 1,
"several": 1,
"she": 1,
"should": 1,
"show": 1,
"side": 1,
"since": 1,
"sincere": 1,
"six": 1,
"sixty": 1,
"so": 1,
"some": 1,
"somehow": 1,
"someone": 1,
"something": 1,
"sometime": 1,
"sometimes": 1,
"somewhere": 1,
"still": 1,
"such": 1,
"system": 1,
"take": 1,
"ten": 1,
"than": 1,
"that": 1,
"the": 1,
"their": 1,
"them": 1,
"themselves": 1,
"then": 1,
"thence": 1,
"there": 1,
"thereafter": 1,
"thereby": 1,
"therefore": 1,
"therein": 1,
"thereupon": 1,
"these": 1,
"they": 1,
"thick": 1,
"thin": 1,
"third": 1,
"this": 1,
"those": 1,
"though": 1,
"three": 1,
"through": 1,
"throughout": 1,
"thru": 1,
"thus": 1,
"to": 1,
"together": 1,
"too": 1,
"top": 1,
"toward": 1,
"towards": 1,
"twelve": 1,
"twenty": 1,
"two": 1,
"un": 1,
"under": 1,
"until": 1,
"up": 1,
"upon": 1,
"us": 1,
"very": 1,
"via": 1,
"was": 1,
"we": 1,
"well": 1,
"were": 1,
"what": 1,
"whatever": 1,
"when": 1,
"whence": 1,
"whenever": 1,
"where": 1,
"whereafter": 1,
"whereas": 1,
"whereby": 1,
"wherein": 1,
"whereupon": 1,
"wherever": 1,
"whether": 1,
"which": 1,
"while": 1,
"whither": 1,
"who": 1,
"whoever": 1,
"whole": 1,
"whom": 1,
"whose": 1,
"why": 1,
"will": 1,
"with": 1,
"within": 1,
"without": 1,
"would": 1,
"yet": 1,
"you": 1,
"your": 1,
"yours": 1,
"yourself": 1,
"yourselves": 1,
"able": 1,
"abst": 1,
"accordance": 1,
"according": 1,
"accordingly": 1,
"act": 1,
"actually": 1,
"added": 1,
"adj": 1,
"affected": 1,
"affecting": 1,
"affects": 1,
"ah": 1,
"announce": 1,
"anybody": 1,
"anymore": 1,
"anyways": 1,
"apparently": 1,
"approximately": 1,
"aren": 1,
"arent": 1,
"arise": 1,
"aside": 1,
"ask": 1,
"asking": 1,
"auth": 1,
"available": 1,
"away": 1,
"awfully": 1,
"b": 1,
"begin": 1,
"beginning": 1,
"beginnings": 1,
"begins": 1,
"believe": 1,
"biol": 1,
"brief": 1,
"briefly": 1,
"c": 1,
"ca": 1,
"came": 1,
"cause": 1,
"causes": 1,
"certain": 1,
"certainly": 1,
"com": 1,
"come": 1,
"comes": 1,
"contain": 1,
"containing": 1,
"contains": 1,
"d": 1,
"date": 1,
"did": 1,
"didnt": 1,
"different": 1,
"does": 1,
"doesnt": 1,
"doing": 1,
"dont": 1,
"downwards": 1,
"e": 1,
"ed": 1,
"edu": 1,
"effect": 1,
"eighty": 1,
"end": 1,
"ending": 1,
"especially": 1,
"et": 1,
"etal": 1,
"everybody": 1,
"ex": 1,
"f": 1,
"far": 1,
"ff": 1,
"fifth": 1,
"fix": 1,
"followed": 1,
"following": 1,
"follows": 1,
"forth": 1,
"furthermore": 1,
"g": 1,
"gave": 1,
"gets": 1,
"getting": 1,
"given": 1,
"gives": 1,
"giving": 1,
"goes": 1,
"gone": 1,
"got": 1,
"gotten": 1,
"h": 1,
"happens": 1,
"hardly": 1,
"havent": 1,
"having": 1,
"hed": 1,
"heres": 1,
"herself": 1,
"hes": 1,
"hi": 1,
"hid": 1,
"himself": 1,
"hither": 1,
"home": 1,
"howbeit": 1,
"id": 1,
"ill": 1,
"im": 1,
"immediate": 1,
"immediately": 1,
"importance": 1,
"important": 1,
"index": 1,
"information": 1,
"instead": 1,
"invention": 1,
"inward": 1,
"isnt": 1,
"itd": 1,
"itll": 1,
"itself": 1,
"ive": 1,
"j": 1,
"just": 1,
"k": 1,
"keepkeeps": 1,
"kept": 1,
"kg": 1,
"km": 1,
"know": 1,
"known": 1,
"knows": 1,
"l": 1,
"largely": 1,
"lately": 1,
"later": 1,
"lest": 1,
"let": 1,
"lets": 1,
"like": 1,
"liked": 1,
"likely": 1,
"line": 1,
"little": 1,
"ll": 1,
"look": 1,
"looking": 1,
"looks": 1,
"m": 1,
"mainly": 1,
"make": 1,
"makes": 1,
"maybe": 1,
"mean": 1,
"means": 1,
"meantime": 1,
"merely": 1,
"mg": 1,
"million": 1,
"miss": 1,
"ml": 1,
"mr": 1,
"mrs": 1,
"mug": 1,
"myself": 1,
"n": 1,
"na": 1,
"nay": 1,
"nd": 1,
"near": 1,
"nearly": 1,
"necessarily": 1,
"necessary": 1,
"need": 1,
"needs": 1,
"new": 1,
"ninety": 1,
"non": 1,
"nonetheless": 1,
"normally": 1,
"nos": 1,
"noted": 1,
"o": 1,
"obtain": 1,
"obtained": 1,
"obviously": 1,
"oh": 1,
"ok": 1,
"okay": 1,
"old": 1,
"omitted": 1,
"ones": 1,
"ord": 1,
"ought": 1,
"outside": 1,
"overall": 1,
"owing": 1,
"p": 1,
"page": 1,
"pages": 1,
"particular": 1,
"particularly": 1,
"past": 1,
"placed": 1,
"plus": 1,
"poorly": 1,
"possible": 1,
"possibly": 1,
"potentially": 1,
"pp": 1,
"predominantly": 1,
"present": 1,
"previously": 1,
"primarily": 1,
"probably": 1,
"promptly": 1,
"proud": 1,
"provides": 1,
"q": 1,
"que": 1,
"quickly": 1,
"quite": 1,
"qv": 1,
"r": 1,
"ran": 1,
"rd": 1,
"readily": 1,
"really": 1,
"recent": 1,
"recently": 1,
"ref": 1,
"refs": 1,
"regarding": 1,
"regardless": 1,
"regards": 1,
"related": 1,
"relatively": 1,
"research": 1,
"respectively": 1,
"resulted": 1,
"resulting": 1,
"results": 1,
"right": 1,
"run": 1,
"s": 1,
"said": 1,
"saw": 1,
"say": 1,
"saying": 1,
"says": 1,
"sec": 1,
"section": 1,
"seeing": 1,
"seen": 1,
"self": 1,
"selves": 1,
"sent": 1,
"seven": 1,
"shall": 1,
"shed": 1,
"shell": 1,
"shes": 1,
"shouldnt": 1,
"showed": 1,
"shown": 1,
"showns": 1,
"shows": 1,
"significant": 1,
"significantly": 1,
"similar": 1,
"similarly": 1,
"slightly": 1,
"somebody": 1,
"somethan": 1,
"somewhat": 1,
"soon": 1,
"sorry": 1,
"specifically": 1,
"specified": 1,
"specify": 1,
"specifying": 1,
"stop": 1,
"strongly": 1,
"sub": 1,
"substantially": 1,
"successfully": 1,
"sufficiently": 1,
"suggest": 1,
"sup": 1,
"sure": 1,
"t": 1,
"taken": 1,
"taking": 1,
"tell": 1,
"tends": 1,
"th": 1,
"thank": 1,
"thanks": 1,
"thanx": 1,
"thatll": 1,
"thats": 1,
"thatve": 1,
"theirs": 1,
"thered": 1,
"therell": 1,
"thereof": 1,
"therere": 1,
"theres": 1,
"thereto": 1,
"thereve": 1,
"theyd": 1,
"theyll": 1,
"theyre": 1,
"theyve": 1,
"think": 1,
"thou": 1,
"thoughh": 1,
"thousand": 1,
"throug": 1,
"til": 1,
"tip": 1,
"took": 1,
"tried": 1,
"tries": 1,
"truly": 1,
"try": 1,
"trying": 1,
"ts": 1,
"twice": 1,
"u": 1,
"unfortunately": 1,
"unless": 1,
"unlike": 1,
"unlikely": 1,
"unto": 1,
"ups": 1,
"use": 1,
"used": 1,
"useful": 1,
"usefully": 1,
"usefulness": 1,
"uses": 1,
"using": 1,
"usually": 1,
"v": 1,
"value": 1,
"various": 1,
"ve": 1,
"viz": 1,
"vol": 1,
"vols": 1,
"vs": 1,
"w": 1,
"want": 1,
"wants": 1,
"wasnt": 1,
"way": 1,
"wed": 1,
"welcome": 1,
"went": 1,
"werent": 1,
"weve": 1,
"whatll": 1,
"whats": 1,
"wheres": 1,
"whim": 1,
"whod": 1,
"wholl": 1,
"whomever": 1,
"whos": 1,
"widely": 1,
"willing": 1,
"wish": 1,
"wont": 1,
"words": 1,
"world": 1,
"wouldnt": 1,
"www": 1,
"x": 1,
"y": 1,
"yes": 1,
"youd": 1,
"youll": 1,
"youre": 1,
"youve": 1,
"z": 1,
"zero": 1,
"hadnt": 1,
"hell": 1,
"hows": 1,
"mustnt": 1,
"oursourselves": 1,
"shant": 1,
"whens": 1,
"whys": 1,
"": 1,
"dear": 1,
"tis": 1,
"twas": 1,
"aint": 1,
"allow": 1,
"allows": 1,
"apart": 1,
"appear": 1,
"appreciate": 1,
"appropriate": 1,
"associated": 1,
"best": 1,
"better": 1,
"cmon": 1,
"cs": 1,
"changes": 1,
"clearly": 1,
"concerning": 1,
"consequently": 1,
"consider": 1,
"considering": 1,
"corresponding": 1,
"course": 1,
"currently": 1,
"definitely": 1,
"described": 1,
"despite": 1,
"entirely": 1,
"exactly": 1,
"example": 1,
"going": 1,
"greetings": 1,
"hello": 1,
"help": 1,
"hopefully": 1,
"ignored": 1,
"inasmuch": 1,
"indicate": 1,
"indicated": 1,
"indicates": 1,
"inner": 1,
"insofar": 1,
"keeps": 1,
"novel": 1,
"presumably": 1,
"reasonably": 1,
"second": 1,
"secondly": 1,
"sensible": 1,
"seriously": 1,
"thorough": 1,
"thoroughly": 1,
"wonder": 1
}
/**
* @description This is a naive text summarization algorithm in javascript
* @author Spiff Jekey-Green
* December, 2022
*/
class SummaryTool {
// Naive method for splitting a text into sentences
split_content_to_sentences(content) {
return content.replace(/\\n/g, ". ").split(". ");
}
// Naive method for splitting a text into paragraphs
split_content_to_paragraphs(content) {
return content.split("\n\n");
}
// Calculate the intersection between 2 sentences
sentences_intersection(sent1, sent2) {
// split the sentence into words/tokens
const s1 = new Set(sent1.split(" "));
const s2 = new Set(sent2.split(" "));
// If there is not intersection, just return 0
if ((s1.size + s2.size) === 0) return 0;
// We normalize the result by the average number of words
const intersection = new Set([...s1].filter(i => s2.has(i)));
return intersection.size / ((s1.size + s2.size) / 2);
}
// Format a sentence - remove all non-alphbetic chars from the sentence
// We'll use the formatted sentence as a key in our sentences dictionary
format_sentence(sentence) {
return sentence.replace(/[\W_]+/g, "");
}
// Convert the content into a dictionary <K, V>
// k = The formatted sentence
// V = The rank of the sentence
get_sentences_ranks(content) {
// Split the content into sentences
const sentences = this.split_content_to_sentences(content);
// Calculate the intersection of every two sentences
const n = sentences.length;
const values = Array(n).fill(Array(n).fill(0));
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
values[i][j] = this.sentences_intersection(sentences[i], sentences[j]);
}
}
// Build the sentences dictionary
// The score of a sentences is the sum of all its intersection
const sentences_dic = {};
for (let i = 0; i < n; i++) {
let score = 0;
for (let j = 0; j < n; j++) {
if (i === j) continue;
score += values[i][j];
}
sentences_dic[this.format_sentence(sentences[i])] = score;
}
return sentences_dic;
}
// Return the best sentence in a paragraph
get_best_sentence(paragraph, sentences_dic) {
// Split the paragraph into sentences
const sentences = this.split_content_to_sentences(paragraph);
// Ignore short paragraphs
if (sentences.length < 2) return "";
// Get the best sentence according to the sentences dictionary
let best_sentence = "";
let max_value = 0;
for (let s of sentences) {
const strip_s = this.format_sentence(s);
if (strip_s && sentences_dic[strip_s] > max_value) {
max_value = sentences_dic[strip_s];
best_sentence = s;
}
}
return best_sentence;
}
// Build the summary
get_summary(title, content, sentences_dic) {
// Split the content into paragraphs
const paragraphs = this.split_content_to_paragraphs(content);
// Add the title
const summary = [];
summary.push(title.trim());
// summary.push("");
// Add the best sentence from each paragraph
paragraphs.forEach(p => {
const sentence = this.get_best_sentence(p, sentences_dic).trim();
if (sentence) {
summary.push(sentence);
};
});
return summary.join("\n");
}
}
function main() {
var title = "Swayy is a beautiful new dashboard for discovering and curating online content [Invites]";
var content = "";
content += "Lior Degani, the Co-Founder and head of Marketing of Swayy, pinged me last week when I was in California to tell me about his startup and give me beta access. I heard his pitch and was skeptical. I was also tired, cranky and missing my kids – so my frame of mind wasn't the most positive.\n";
content += "I went into Swayy to check it out, and when it asked for access to my Twitter and permission to tweet from my account, all I could think was, \"If this thing spams my Twitter account I am going to bitch-slap him all over the Internet.\" Fortunately that thought stayed in my head, and not out of my mouth.\n";
content += "One week later, I'm totally addicted to Swayy and glad I said nothing about the spam (it doesn't send out spam tweets but I liked the line too much to not use it for this article). I pinged Lior on Facebook with a request for a beta access code for TNW readers. I also asked how soon can I write about it. It's that good. Seriously. I use every content curation service online. It really is That Good.\n";
content += "What is Swayy? It's like Percolate and LinkedIn recommended articles, mixed with trending keywords for the topics you find interesting, combined with an analytics dashboard that shows the trends of what you do and how people react to it. I like it for the simplicity and accuracy of the content curation.\n";
content += "Everything I'm actually interested in reading is in one place – I don't have to skip from another major tech blog over to Harvard Business Review then hop over to another major tech or business blog. It's all in there. And it has saved me So Much Time\n\n";
content += "After I decided that I trusted the service, I added my Facebook and LinkedIn accounts. The content just got That Much Better. I can share from the service itself, but I generally prefer reading the actual post first – so I end up sharing it from the main link, using Swayy more as a service for discovery.\n";
content += "I'm also finding myself checking out trending keywords more often (more often than never, which is how often I do it on Twitter.com).\n\n\n";
content += "The analytics side isn't as interesting for me right now, but that could be due to the fact that I've barely been online since I came back from the US last weekend. The graphs also haven't given me any particularly special insights as I can't see which post got the actual feedback on the graph side (however there are numbers on the Timeline side.) This is a Beta though, and new features are being added and improved daily. I'm sure this is on the list. As they say, if you aren't launching with something you're embarrassed by, you've waited too long to launch.\n";
content += "It was the suggested content that impressed me the most. The articles really are spot on – which is why I pinged Lior again to ask a few questions:\n";
content += "How do you choose the articles listed on the site? Is there an algorithm involved? And is there any IP?\n";
content += "Yes, we're in the process of filing a patent for it. But basically the system works with a Natural Language Processing Engine. Actually, there are several parts for the content matching, but besides analyzing what topics the articles are talking about, we have machine learning algorithms that match you to the relevant suggested stuff. For example, if you shared an article about Zuck that got a good reaction from your followers, we might offer you another one about Kevin Systrom (just a simple example).\n";
content += "Who came up with the idea for Swayy, and why? And what's your business model?\n";
content += "Our business model is a subscription model for extra social accounts (extra Facebook / Twitter, etc) and team collaboration.\n";
content += "The idea was born from our day-to-day need to be active on social media, look for the best content to share with our followers, grow them, and measure what content works best.\n";
content += "Who is on the team?\n";
content += "Ohad Frankfurt is the CEO, Shlomi Babluki is the CTO and Oz Katz does Product and Engineering, and I [Lior Degani] do Marketing. The four of us are the founders. Oz and I were in 8200 [an elite Israeli army unit] together. Emily Engelson does Community Management and Graphic Design.\n";
content += "If you use Percolate or read LinkedIn's recommended posts I think you'll love Swayy.\n";
content += "Want to try Swayy out without having to wait? Go to this secret URL and enter the promotion code thenextweb . The first 300 people to use the code will get access.\n";
content += "Image credit: Thinkstock";
const st = new SummaryTool();
const sentences_dic = st.get_sentences_ranks(content);
const summary = st.get_summary(title, content, sentences_dic);
console.log("Content: ", content);
console.log("\n\n\n\n");
console.log("Summary: ", summary);
}
main();
@SpiffGreen
Copy link
Author

Two packages are need for the nlp_text_summarizer.js code. First do;

npm install compromise isstopword

Then run the app,

node nlp_text_summarizer.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment