How can this Javascript be expressed more succinctly? - javascript

I have some Python code that I'm porting to Javascript:
word_groups = defaultdict(set)
for sentence in sentences:
sentence.tokens = stemmed_words(sentence.str_)
for token in sentence.tokens:
word_groups[sentence.actual_val].add(token)
I don't know a lot about Javascript, so this was the best I could do:
var word_groups = {}
for(var isent = 0; isent < sentences.length; isent++) {
var sentence = sentences[isent]
sentence.tokens = stemmed_words(sentence.str_)
for(var itoken = 0; itoken < sentence.tokens.length; itoken++) {
var token = sentence.tokens[itoken]
if(!(sentence.actual_val in word_groups))
word_groups[sentence.actual_val] = []
var group = word_groups[sentence.actual_val]
if(!(token in group))
group.push(token)
}
}
Can anyone suggest ways to make the javascript code look more like the python?

I'm going to assume that if you're using an environment where forEach is available, reduce and Object.keys are available as well. (e.g. ECMAScript >= 1.8.5):
var word_groups = sentences.reduce(function (groups, sentence) {
var val = sentence.actual_val
var group = groups[val] = groups[val] || []
stemmed_words(sentence.str_).forEach(function (t) {
if (!(t in group)) group.push(t)
})
return groups
}, {})

It's quite possible that I've misinterpreted what your Python code does, but assuming you're after word counts, I'd write it as follows:
var word_groups = {}
sentences.forEach(function (sentence) {
sentence.tokens = stemmed_words(sentence.str_)
sentence.tokens.forEach(function (token) {
var val = sentence.actual_val
word_groups[val] = (word_groups[val] || 0) + 1
})
})
The above will fail should the word "constructor" appear in the input. It's possible to work around this JavaScript quirk:
var word_groups = {}
sentences.forEach(function (sentence) {
sentence.tokens = stemmed_words(sentence.str_)
sentence.tokens.forEach(function (token) {
var val = sentence.actual_val
if (!word_groups.hasOwnProperty(val)) word_groups[val] = 0
word_groups[val] += 1
})
})

If you're not definitely in Javascript 1.6 or higher (notable IE 8 has Javascript 1.5) you may want jQuery as a compatibility layer. For example $.each(a, f) is compatible with a.forEach(f).

Related

Why can't I ready this Object properties?

I have been trying to figure this out for a couple hours now.
I am receiving a JSON post (Shopify), I'm working in app script via doPost(e) web app, but maybe that's irrelevant.
Here's how it looks when received
notice the weird backslashes in postData.contents (it was stringified to show in my console)...
{"contentLength":6450,"queryString":"","postData":{"contents":"{\"id\":820982911946154508,\"
// clipped because stackoverflow doesn't like it
Here are a bunch of things I've tried
e.postData.contents;
// => {"id":820982911946154508,"email":"jon#doe.ca",...} (Clipped here too)
//Great, looks like a proper object...BUT...
var order = e.postData.contents;
//order.id => undefined
var order = JSON.parse(e.postData.contents);
//order.id => undefined
var order = JSON.parse(JSON.stringify(e.postData.contents));
//order.id => undefined
This is how I convert all numbers over 9 digits to strings
Not the best, maybe if a substring contains commas or colons that surround a number, it will break it.
function testStringNum () {
var test = {"id":820982911946154500,"email":"jon#doe.ca","closed_at":null,"created_at":"2020-07-01T10:01:14-07:00","updated_at":"2020-07-01T10:01:14-07:00","number":234 };
var result = jsonStringNums(test);
}
function jsonStringNums(obj) {
var objString = JSON.stringify(obj);
var objArray = objString.split(/[:,]+/);
for (let index in objArray) {
var regExp = new RegExp(/^(?=.*\d)[\d ]+$/);
var isNum = regExp.test(objArray[index]);
if (isNum && objArray[index].length >= 9) {
objString = objString.replace(objArray[index], '"'+objArray[index]+'"');
}
}
return JSON.parse(objString);
}
Had to handle duplicate data in the array, so this is better
function jsonStringNums(obj) {
var objString = JSON.stringify(obj);
var objArrayAll = objString.split(/[:,]+/);
var objArray = objArrayAll.reduce((unique,item) => {
return unique.includes(item) ? unique : [...unique,item] }, []);
var rxAllNumAndSpaces = new RegExp(/^(?=.*\d)[\d ]+$/);
for (let index in objArray) {
var isNum = rxAllNumAndSpaces.test(objArray[index]);
if (isNum && objArray[index].length >= 8) {
var replacement = '\"'+objArray[index]+'\"';
var rxAllVarInsts = new RegExp(objArray[index], 'g');
objString = objString.replace(rxAllVarInsts, replacement);
}
}
try {
return JSON.parse(objString);
} catch (err) { Logger.log('you done messed up your object '+err);return obj; }
}

UTF-8 support for regular expression in Javascript

I am trying to create a Javascript function that would find all positions for a set of patterns inside a UTF-8 string. For example:
I have a string "detaj" (it's a transcription written with International Phonetic Alphabet symbols, so I need a full UTF-8 support).
And I have an array of patterns: ["(?!dʒ)d", "(?!tʃ)t"] (each string is also UTF-8 encoded).
I need to find the position of each pattern and obtain the following array:
[0] => [0, "(?!dʒ)d"],
[1] => [2, "(?!tʃ)t"]
0 - is the position of the symbol "d", 2 - is the position of the symbol "t".
I started with this function:
https://stackoverflow.com/a/3410557/2006215
var str = "I learned to play the Ukulele in Lebanon."
var regex = /le/gi, result, indices = [];
while ( (result = regex.exec(str)) ) {
indices.push(result.index);
}
And I changed it to something like this:
function getAllIndicesOfArrayOfStringInsideStringRegex (sounds_regex_array, word_transcription) {
var allIndices = [];
for (var i = 0; i < sounds_regex_array.length; i++) {
var currentSoundRegex = sounds_regex_array[i];
// straightforward approach doesn't work:
//var pattern = new RegExp(currentSoundRegex, "g");
// hexEncode is taken from here - https://stackoverflow.com/a/21648161/2006215 - doesn't work:
//var pattern = new RegExp(currentSoundRegex.hexEncode, "g");
// I'm trying to use utf8.js plugin from here - https://github.com/mathiasbynens/utf8.js - doesn't work:
var pattern = new RegExp(utf8.encode(currentSoundRegex), "g");
var indices = getIndicesOfRegex (pattern, word_transcription);
for (var j = 0; j < indices.length; j++) {
allIndices.push([indices[j], currentSoundRegex ]);
}
}
return allIndices;
}
function getIndicesOfRegex (regex, str) {
var result, indices = [];
while (result = regex.exec(str)) {
indices.push(result.index);
}
return indices;
}
Anybody has any ideas?
UPDATE: I take both the transcription and the regex patterns from json file that I generate with PHP from UTF-8 strings. I am not sure how to call this, but it's not UTF-8. In any case it doesn't work with my Javascript function.
var questions = [{"word":"sorte","word_transcription":"s\u0254\u0281t","sounds_array":["d","t"],"sounds_regex_array":["(?!d\u0292)d","(?!t\u0283)t"]}];
I found where the problem was.
The error was triggered because I tried to execute lookbehind in Javascript, which is not supported.
The workaround for custom lookbehind functions is proposed here - http://blog.stevenlevithan.com/archives/javascript-regex-lookbehind
But finally I just did my own modifications of the code. The above functions require XRegExp library, which is pretty heavy.
My solution:
function getIndicesOfRegex (currentSoundRegex, pattern, str) {
var result, indices = [];
while (result = pattern.exec(str)) {
if ((currentSoundRegex === "ʒ") && (result.index > 0) && (str.substring(result.index-1, result.index) === "d")) { continue; }
if ((currentSoundRegex === "ʃ") && (result.index > 0) && (str.substring(result.index-1, result.index) === "t")) { continue; }
indices.push(result.index);
}
return indices;
}
function getAllIndicesOfArrayOfStringInsideStringRegex (sounds_array, sounds_regex_array, word_transcription) {
var allIndices = [];
for (var i = 0; i < sounds_regex_array.length; i++) {
var currentSoundRegex = sounds_regex_array[i];
// lookbehind doesn't work in Javascript:
// possible workaround - http://blog.stevenlevithan.com/archives/javascript-regex-lookbehind
if (currentSoundRegex === "(?<!d)ʒ") {
currentSoundRegex = "ʒ";
}
if (currentSoundRegex === "(?<!t)ʃ") {
currentSoundRegex = "ʃ";
}
var pattern = new RegExp(currentSoundRegex, "g");
var indices = getIndicesOfRegex (currentSoundRegex, pattern, word_transcription);
var currentSound = sounds_array[i];
for (var j = 0; j < indices.length; j++) {
allIndices.push([indices[j], currentSound]);
}
}
return allIndices;
}

How to test if an id is present in an associative array

I'm am starting in javascript. I'm trying to do a little program that make a statistic upon the number of answer found in a text document.
The situation is this: each question has one id, e.g 8000001 and W if answer is good or R if answer is not good, e.g for an user answer is 8000001W. I have many user so many question of the same id. I want to get number of good answers per questions. E.g id: 800001 have W: 24 and "R": 5.
I have split the answer into id for 8000001 and ans for W or R. I wanted to create an associative table to get question[id]=["W": 0, "R": 0]. But I'm blocking on this. I've tried this code:
var tab = [];
tab[0] = [];
tab[0] = ['8000001W', '8000002W', '8000003W', '8000004R', '8000005W', '8000006R'];
tab[1] = [];
tab[1] = ['8000001R', '8000002W', '8000003R', '8000004W', '8000005R', '8000006W'];
var question = [];
var id;
for (var i=0;i<tab.length;i++) {
document.write("<dl><dt>tableau n° "+i+"<\/dt>");
for (var propriete in tab[i]) {
id = tab[i][propriete].slice(0,7);
var ans = tab[i][propriete].slice(7,8);
question[id] = [];
if(question[id]){
incrementResp.call(rep, ans);
}else{
var rep = initResp(ans);
question[id] = rep;
}
}
document.write("<\/dl>");
}
function incrementResp(type){
this.this++;
}
function initResp(t){
rep = [];
rep.W = (t=='W'?1:0);
rep.R = (t=='R'?1:0);
}
Based on what your want finally, the 'question' should be used as an object literal, defined as question = {} (similar to association array), what you defined here is an array literal. You can check this for more information about different types of literals in JavaScript:
https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Values,_variables,_and_literals#Literals
In terms of your code, you can simple do like this:
if (question[id]) {
question[id][ans] += 1;
}
else {
var rep = initResp(ans);
question[id] = rep;
}
Also your 'initResp' function better to return an object literal 'rep', not as an array literal:
function initResp(t){
var rep = {};
rep.W = (t=='W'?1:0);
rep.R = (t=='R'?1:0);
return rep;
}
For an "associative array" in JavaScript, use a regular object. In the code below, "results" is one of these objects. It has two keys, "W" and "R" that point to numbers starting at 0. Just iterate through your answer arrays and continuously increment the correct key.
There are two ways to access a key in an object: 1) using brackets, 2) using "dot" notation. In the loop I use brackets because 'key' is a variable--it will resolve to "W" or "R" and therefore access the "W" or "R" key in that object. In the final two lines I use dot notation because "W" and "R" are literally the keys I want to access. It would also work if I did this instead: results['W']++ and results['R']++.
var tab = [];
tab[0] = ['8000001W', '8000002W', '8000003W', '8000004R', '8000005W', '8000006R'];
tab[1] = ['8000001R', '8000002W', '8000003R', '8000004W', '8000005R', '8000006W'];
var results = {
W: 0,
R: 0
};
// go through each tab
for (var tabIdx = 0; tabIdx < tab.length; tabIdx++) {
// go through each answer and apppend to an object that keeps the results
for (var i = 0; i < tab[tabIdx].length; i++) {
var answer = tab[tabIdx][i];
// the last character in the string is the "key", (W or R)
var key = answer.slice(-1);
// append to the results object
results[key]++;
}
}
console.log(results);
console.log(results.W); // 7
console.log(results.R); // 5
Open up your development console (on Chrome it's F12) to see the output.
This is how i resolved my problem for associative array.
var tab = [];
tab[0] = ['8000001W', '8000002W', '8000003W', '8000004R', '8000005W', '8000006R'];
tab[1] = ['8000001R', '8000002W', '8000003R', '8000004W', '8000005R', '8000006W'];
tab[2] = ['8000001R', '8000002W', '8000003R', '8000004W', '8000005R', '8000006W'];
var question = {};
for (var tabIndex = 0; tabIndex < tab.length; tabIndex++) {
for (var i = 0; i < tab[tabIndex].length; i++) {
var answer = tab[tabIndex][i];
var id = answer.slice(0,7);
var ans = answer.slice(-1);
if (question[id]) {
question[id][ans] += 1;
}else {
var results = initResp(ans);
question[id] = results;
}
}
}
console.log(question);
function initResp(t) {
var results = [];
results.W = (t === 'W' ? 1 : 0);
results.R = (t === 'R' ? 1 : 0);
//console.log(results);
return results;
}

Is there a way of formatting strings in javascript?

Well , in python or java or ... we do sth like : (the python version )
tmp = "how%s" %("isit")
and now tmp looks like "howisit".
is there any bulit in thing like that in javascript ? ( rather than sprintf )
Thanks
Not build in, but you can make your own templating by extending the String prototype:
String.prototype.template = String.prototype.template ||
function(){
var args = arguments;
function replacer(a){
return args[Number(a.slice(1))-1] || a;
}
return this.replace(/(\$)?\d+/gm,replacer)
};
// usages
'How$1'.template('isit'); //=> Howisit
var greet = new Date('2012/08/08 08:00') < new Date
? ['where','yesterday'] : ['are','today'];
'How $1 you $2?'.template(greet[0],greet[1]); // How where you yesterday?
Nop, there isn't. You can do string concatenation.
var tmp = 'how' + 'isit';
Or replace in other situations. This is a stupid example but you get the idea:
var tmp = 'how{0}'.replace('{0}', 'isit');
No, there is no string formating built in to javascript.
No builtin function is there, however you can very easily build one yourself. The replace function can take a function argument and is the perfect solution for this work. Although be careful with large strings and complicated expressions as this might get slow quickly.
var formatString = function(str) {
// get all the arguments after the first
var replaceWith = Array.prototype.slice.call(arguments, 1);
// simple replacer based on String, Number
str.replace(/%\w/g, function() {
return replaceWith.shift();
});
};
var newString = formatString("how %s %s?", "is", "it");
I think you can use these (simplistic) snippets;
function formatString(s, v) {
var s = (''+ s), ms = s.match(/(%s)/g), m, i = 0;
if (!ms) return s;
while(m = ms.shift()) {
s = s.replace(/(%s)/, v[i]);
i++;
}
return s;
}
var s = formatString("How%s", ["isit"]);
Or;
String.prototype.format = function() {
var s = (""+ this), ms = s.match(/(%s)/g) || [], m, v = arguments, i = 0;
while(m = ms.shift()) {
s = s.replace(/(%s)/, v[i++]);
}
return s;
}
var s = "id:%s, name:%s".format(1,"Kerem");
console.log(s);

how can I refactor this?

As for now, when I prepare my data to be sent by Ajax request to my web app, I just concat my JS arrays (with placing -1 between them as separator - values can be positive only, so -1 means start of new array). This seems a bit ugly for me, so I'm wondering what would be best practice to refator this.
var online1 = [];
var online2 = [];
var online3 = [];
var online4 = [];
for(i = 0 ; i < listOfPlayers.length ; i++) {
var player = listOfPlayers[i].href;
var uid = player.substring(player.lastIndexOf('=') + 1);
if(onlineStatus[i].className == "online1"){
online1.push(uid);
}
if(onlineStatus[i].className == "online2"){
online2.push(uid);
}
if(onlineStatus[i].className == "online3"){
online3.push(uid);
}
if(onlineStatus[i].className == "online4"){
online4.push(uid);
}
}
online1.push(-1);
online2.push(-1);
online3.push(-1);
online4.push(-1);
var result = online1.concat(online2, online3, online4);
//...
ajaxRequest.send("result="+result);
You could do two things:
Use an object, stringify it using JSON.stringify. You can parse it using JSON.parse, even server-side solutions exist. JSON is available in recent browsers and as library.
Make the if generic.
E.g.:
var online = {1: [],
2: [],
3: [],
4: []};
for(i = 0 ; i < listOfPlayers.length ; i++) {
var player = listOfPlayers[i].href;
var uid = player.substring(player.lastIndexOf('=') + 1);
var number = onlineStatus[i].className.substring(6);
online[number].push(uid);
}
var result = JSON.stringify(online);
//...
ajaxRequest.send("result="+result);

Categories

Resources