Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite code to ES6 syntax #145

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
"env": {
"node": true
},
"parserOptions": {
"ecmaVersion": 8
},
"extends": "eslint:recommended"
}
103 changes: 49 additions & 54 deletions build/build.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,74 @@
var async = require('async');
var fs = require('fs');
var path = require('path');
const fs = require('fs');
const path = require('path');

// File paths
var EMOJI_PATH = path.resolve(__dirname, 'Emoji_Sentiment_Data_v1.0.csv');
var RESULT_PATH = path.resolve(__dirname, 'emoji.json');
const EMOJI_PATH = path.resolve(__dirname, 'Emoji_Sentiment_Data_v1.0.csv');
const RESULT_PATH = path.resolve(__dirname, 'emoji.json');

/**
* Read emoji data from original format (CSV).
* @param {object} hash Result hash
* @param {Function} callback Callback
* @return {void}
* @param {object} hash Result hash.
* @return {object} hash Result hash.
*/
function processEmoji(hash, callback) {
const processEmoji = async hash => {
// Read file
fs.readFile(EMOJI_PATH, 'utf8', function (err, data) {
if (err) return callback(err);

try {
const data = await fs.readFileSync(EMOJI_PATH, 'utf8');
// Split data by new line
data = data.split(/\n/);

const lines = data.split(/\n/);
// Iterate over dataset and add to hash
for (var i in data) {
var line = data[i].split(',');

for (const i in lines) {
const line = lines[i].split(',');
// Validate line
if (i == 0) continue; // Label
if (line.length !== 9) continue; // Invalid
if (i === '0' || i === 0 || line.length !== 9) continue;
// ^ Label ^ Label ^ Invalid

// Establish sentiment value
var emoji = String.fromCodePoint(line[1]);
var occurences = line[2];
var negCount = line[4];
var posCount = line[6];
var score = (posCount / occurences) - (negCount / occurences);
var sentiment = Math.floor(5 * score);
const emoji = String.fromCodePoint(line[1]);
const occurences = line[2];
const negCount = line[4];
const posCount = line[6];
const score = posCount / occurences - negCount / occurences;
const sentiment = Math.floor(5 * score);

// Validate score
if (Number.isNaN(sentiment)) continue;
if (sentiment === 0) continue;
if (Number.isNaN(sentiment) || sentiment === 0) continue;

// Add to hash
hash[emoji] = sentiment;
}

callback(null, hash);
});
}
return hash;
} catch (e) {
throw new Error(e);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove the try catch blocks? I think you can simplify the nesting here and right now you are just throwing the error you just caught. Also, the nested catch, throw prepends an Error: in the message.

};

/**
* Write sentiment score hash to disk.
* @param {object} hash Result hash
* @param {Function} callback Callback
* @return {void}
* @return {object} hash Result hash
*/
function finish(hash, callback) {
var result = JSON.stringify(hash, null, 4);
fs.writeFile(RESULT_PATH, result, function (err) {
if (err) return callback(err);
callback(null, hash);
});
}
const finish = async hash => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest we rename function to something like writeJSON or writeEmojiHash and move the Complete: ${Object.keys(hash).length} entries. into this utility function. Then you don't even need to return the hash.

const result = JSON.stringify(hash, null, 4);
try {
await fs.writeFileSync(RESULT_PATH, result);
return hash;
} catch (e) {
throw new Error(e);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove try catch block.

}
};

// Execute build process
async.waterfall([
function (cb) {
cb(null, {});
},
processEmoji,
finish
], function(err, result) {
if (err) throw new Error(err);
process.stderr.write(
'Complete: ' +
Object.keys(result).length +
' entries.\n'
);
});
const build = async () => {
try {
let hash = {};
hash = await processEmoji(hash);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you don't really need to pass hash into the processEmoji function. Suggest something like:

try {
  const hash = await processEmoji();
  await finish(hash);
} catch (e) {
  console.error(e)
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You right, I didn't realize this.

hash = await finish(hash);
process.stderr.write(
`Complete: ${Object.keys(hash).length} entries.\n`
);
} catch (e) {
throw new Error(e);
}
};
build();
7 changes: 5 additions & 2 deletions languages/en/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const labels = require('./labels.json');
const scoringStrategy = require('./scoring-strategy');

module.exports = {
labels: require('./labels.json'),
scoringStrategy: require('./scoring-strategy')
labels,
scoringStrategy
};
20 changes: 11 additions & 9 deletions languages/en/scoring-strategy.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
var negators = require('./negators.json');
const negators = require('./negators.json');

module.exports = {
apply: function(tokens, cursor, tokenScore) {
if (cursor > 0) {
var prevtoken = tokens[cursor - 1];
if (negators[prevtoken]) {
tokenScore = -tokenScore;
}
const apply = (tokens, cursor, tokenScore) => {
if (cursor > 0) {
const prevtoken = tokens[cursor - 1];
if (negators[prevtoken]) {
tokenScore = -tokenScore;
}
return tokenScore;
}
return tokenScore;
};

module.exports = {
apply
};
173 changes: 86 additions & 87 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,98 +1,97 @@
var tokenize = require('./tokenize');
var languageProcessor = require('./language-processor');
const tokenize = require('./tokenize');
const languageProcessor = require('./language-processor');

/**
* Constructor
* @param {Object} options - Instance options
*/
var Sentiment = function (options) {
this.options = options;
};

/**
* Registers the specified language
*
* @param {String} languageCode
* - Two-digit code for the language to register
* @param {Object} language
* - The language module to register
*/
Sentiment.prototype.registerLanguage = function (languageCode, language) {
languageProcessor.addLanguage(languageCode, language);
};

/**
* Performs sentiment analysis on the provided input 'phrase'.
*
* @param {String} phrase
* - Input phrase
* @param {Object} opts
* - Options
* @param {Object} opts.language
* - Input language code (2 digit code), defaults to 'en'
* @param {Object} opts.extras
* - Optional sentiment additions to AFINN (hash k/v pairs)
* @param {function} callback
* - Optional callback
* @return {Object}
*/
Sentiment.prototype.analyze = function (phrase, opts, callback) {
// Parse arguments
if (typeof phrase === 'undefined') phrase = '';
if (typeof opts === 'function') {
callback = opts;
opts = {};
class Sentiment {
/**
* Represents a Sentiment.
* @constructor
* @param {Object} options - Instance options.
*/
constructor(options) {
this.options = options;
}
opts = opts || {};

var languageCode = opts.language || 'en';
var labels = languageProcessor.getLabels(languageCode);

// Merge extra labels
if (typeof opts.extras === 'object') {
labels = Object.assign(labels, opts.extras);
/**
* Registers the specified language.
* @param {string} languageCode - Two-digit code for the language to
* register.
* @param {object} language - The language module to register.
*/
registerLanguage(languageCode, language) {
languageProcessor.addLanguage(languageCode, language);
}
/**
* Performs sentiment analysis on the provided input 'phrase'.
* @param {string} phrase - Input phrase
* @param {object} opts - Options
* @param {object} opts.language - Input language code (2 digit code),
* defaults to 'en'
* @param {object} opts.extras - Optional sentiment additions to AFINN
* (hash k/v pairs)
* @param {function} callback - Optional callback
* @return {object}
*/
analyze(phrase = '', opts = { language: 'en' }, callback) {
// Storage objects
const tokens = tokenize(phrase);
let score = 0;
let words = [];
let positive = [];
let negative = [];

// Storage objects
var tokens = tokenize(phrase),
score = 0,
words = [],
positive = [],
negative = [];
// Parse arguments
if (typeof opts === 'function') {
callback = opts;
opts = {};
}

// Iterate over tokens
var i = tokens.length;
while (i--) {
var obj = tokens[i];
if (!labels.hasOwnProperty(obj)) continue;
words.push(obj);
const languageCode = opts.language;
let labels = languageProcessor.getLabels(languageCode);

// Apply scoring strategy
var tokenScore = labels[obj];
// eslint-disable-next-line max-len
tokenScore = languageProcessor.applyScoringStrategy(languageCode, tokens, i, tokenScore);
if (tokenScore > 0) positive.push(obj);
if (tokenScore < 0) negative.push(obj);
score += tokenScore;
}
// Merge extra labels
if (typeof opts.extras === 'object') {
labels = Object.assign(labels, opts.extras);
}

// Iterate over tokens
let i = tokens.length;
while (i--) {
const obj = tokens[i];
if (!labels.hasOwnProperty(obj)) continue;
words = words.concat(obj);
// Apply scoring strategy
const tokenScore = languageProcessor.applyScoringStrategy(
languageCode,
tokens,
i,
labels[obj]
);
if (tokenScore > 0) {
positive = positive.concat(obj);
}
if (tokenScore < 0) {
negative = negative.concat(obj);
}
score += tokenScore;
}

var result = {
score: score,
comparative: tokens.length > 0 ? score / tokens.length : 0,
tokens: tokens,
words: words,
positive: positive,
negative: negative
};
const result = {
score,
comparative: tokens.length > 0 ? score / tokens.length : 0,
tokens,
words,
positive,
negative
};

// Handle optional async interface
if (typeof callback === 'function') {
process.nextTick(function () {
callback(null, result);
});
} else {
return result;
// Handle optional async interface
if (typeof callback === 'function') {
process.nextTick(() => {
callback(null, result);
});
} else {
return result;
}
}
};
}

module.exports = Sentiment;
Loading