How to stop append string in forEach? - node.js

Here is what I got:
I try to send requests to a service, that's what otherFunction() do. Each request use different parameters loaded from config.events. Then I want to append a randomParam to each event.
config = { events: [...] }
for(let i = 0; i < 100; i++){
config.events.forEach(element => {
// Get a random string
str = '&randomParam=' + Random.word(10)
element.url += str
console.log(element.url)
otherFunction(element)
// the element.url keep append random word 100 times, but I expect only one word append for each loop
})
}
I'd trid create a const var, using deep copy (JSON parse/stringify) and delete element.url and add it again. But none of it could solve my problem.
Actually, I did not know what exactly happend in my code and how this caused.
Current Result:
https://example.com/exampleApi/index.html?testParam=123&randomParam=rbveostvie
https://example.com/exampleApi/index.html?testParam=123&randomParam=rbveostvie&randomParam=bjanckrbjc
https://example.com/exampleApi/index.html?testParam=123&randomParam=rbveostvie&randomParam=bjanckrbjc&randomParam=rbuunvtdux
Expect Result:
https://example.com/exampleApi/index.html?testParam=123&randomParam=rbveostvie
https://example.com/exampleApi/index.html?testParam=123&randomParam=bjanckrbjc
https://example.com/exampleApi/index.html?testParam=123&randomParam=rbuunvtdux

You need to move the random string assignment out of the inner forEach loop.
var config = {
events: [
{
url: "foo"
},
{
url: "baz"
}
]
};
for (let i = 0; i < 100; i++) {
var str = "&randomParam=" + Random.word(10);
config.events.forEach(element => {
otherFunction(Object.assign({}, element, { url: element.url + str }));
});
}
For each i out of n=100 iterations, your inner forEach loop would iterate k=config.events.length times. By moving the random string outside the inner forEach loop, you would ensure that you generate n random strings instead of n * k random strings.
Edit
As per OP updated description, the actual problem is that element.url is being mutated. On every iteration, it will append the random string to the previously set url. Hence, I have updated my answer to include Object.assign({}, element, { url: element.url + str }) which will make sure no object in config.events gets mutated.
Please see below a working example:
function getRandomString() {
return Math.random()
.toString(36)
.substring(7);
}
var config = {
events: [{
url: "foo"
},
{
url: "baz"
}
]
};
function otherFunction(element) {
console.info(element.url);
}
for (let i = 0; i < 5; i++) {
var str = "&randomParam=" + getRandomString();
config.events.forEach(element => {
otherFunction(Object.assign({}, element, {
url: element.url + str
}));
});
}

I assume that config.events is an array of 100 elements, and you want to add a random string to every element.url.
You don't need two loops, one is enough. Remove that outer loop, and you'll get what you want
--EDIT--
According to the comment the author posted later, the outer loop should be moved to wrap the otherFunction call.
config = { events: [...] }
config.events.forEach(element => {
// Get a random string
str = '&randomParam=' + Random.word(10)
element.url += str
for(let i = 0; i < 100; i++){
otherFunction(element)
}
})

Related

NodeJS: Finding a Key that has the most key array matches in a string

I am trying to achieve a small amount of javascript code that is able to locate a key that contains another key with the most array element occurrences in a string. It's a little hard to explain but I have given an example below. I have tried several filters, finds, and lengthy code loops with no luck. Anything would help, thanks :)
const object = {
keyone: {
tags: ["game","video","tv","playstation"]
},
keytwo: {
tags: ["book", "sport", "camping", "out"]
}
};
const string = "This is an example, out playstaion, tv and video games are cool!";
// I am trying to locate the key that contains the most tags in a string.
// In this case the result I am looking for would be "keytwo",
// because it's tags have greater occurances inside the string (playstaion, tv, video, game/s).
This should do it, though you might want to consider adding keyword stemming.
const object = {
keyone: {
tags: ["game", "video", "tv", "playstation"]
},
keytwo: {
tags: ["book", "sport", "camping", "out"]
}
};
const string = "This is an example, out playstaion, tv and video games are cool!";
result = {}
for (const [key, value] of Object.entries(object)) {
result[key] = value.tags.reduce((acc, item) => (acc += (string.match(item) || []).length), 0)
}
console.log(result)
Result:
{ keyone: 3, keytwo: 1 }
Edit:
How to count:
let result_key;
let result_count = 0;
for (const [key, value] of Object.entries(object)) {
const result = value.tags.reduce((acc, item) => (acc += (string.match(item) || []).length), 0);
if(result > result_count) {
result_count = result;
result_key = key;
}
}
console.log(result_key, result_count)
Result:
keyone 3

Prevent nested lists in text-editor (froala)

I need to prevent/disable nested lists in text editor implemented in Angular. So far i wrote a hack that undos a nested list when created by the user. But if the user creates a normal list and presses the tab-key the list is shown as nested for a few milliseconds until my hack sets in back to a normal list. I need something like event.preventDefault() or stopPropagation() on tab-event keydown but unfortunately that event is not tracked for some reason. Also the froala settings with tabSpaces: falseis not showing any difference when it comes to nested list...in summary i want is: if the user creates a list and presses the tab-key that nothing happens, not even for a millisecond. Has anyone an idea about that?
Froala’s support told us, there’s no built-in way to suppress nested list creation. They result from TAB key getting hit with the caret on a list item. However we found a way to get around this using MutationObserver
Basically we move the now nested list item to his former sibling and remove the newly created list. Finally we take care of the caret position.
var observer = new MutationObserver(mutationObserverCallback);
observer.observe(editorNode, {
childList: true,
subtree: true
});
var mutationObserverCallback = function (mutationList) {
var setCaret = function (ele) {
if (ele.nextSibling) {
ele = ele.nextSibling;
}
var range = document.createRange();
var sel = window.getSelection();
range.setStart(ele, 0);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
};
var handleAddedListNode = function (listNode) {
if (! listNode.parentNode) {
return;
}
var parentListItem = listNode.parentNode.closest('li');
if (!parentListItem) {
return;
}
var idx = listNode.children.length - 1;
while (idx >= 0) {
var childNode = listNode.children[idx];
if (parentListItem.nextSibling) {
parentListItem.parentNode.insertBefore(childNode, parentListItem.nextSibling);
} else {
parentListItem.parentNode.appendChild(childNode);
}
--idx;
}
setCaret(parentListItem);
listNode.parentNode.removeChild(listNode);
};
mutationList.forEach(function (mutation) {
var addedNodes = mutation.addedNodes;
if (!addedNodes.length) {
return;
}
for (var i = 0; i < addedNodes.length; i++) {
var currentNode = addedNodes[i];
switch (currentNode.nodeName.toLowerCase()) {
case 'ol':
case 'ul':
handleAddedListNode(currentNode);
break;
// more optimizations
}
}
})
};

Deeply nested data objects in multidimensional object

I have a multidimensional object and using Vue, I am trying to make the inner object reactive.
My object looks like this:
data() {
return {
myObject: {}
}
}
And the filled data looks like this:
myObject: {
1: { // (client)
0: "X", // (index) : (value)
1: "Y"
},
2: {
0: "A",
2: "B"
}
}
If I try using:
let value = "X";
let client = 1;
let index = 1;
let obj = {};
obj[client][index] = value;
this.myObject = Object.assign({}, this.myObject, obj);
It throws an error:
TypeError: Cannot set property '0' of undefined
And if I try below, it overwrites the initial values as it is initially setting the object to {}
let obj = {};
obj[index] = value;
let parentObj = {};
parentObj[client] = obj;
this.myObject = Object.assign({}, this.myObject, parentObj);
What is the proper way of adding the values to the multidimensional object?
In javascript, dim2Thing[1][1] = ... expressions require dim2Thing[1] to exist. This is why you get the error you mentioned. So you can do two expressions, which should work fine:
dim2Thing[1] = dim2Thing[1] || {}
dim2Thing[1][1] = otherThing
For the last block, you mention that it "overwrites the initial values"
I think what's actually happening here is just that Object.assign is not recursive. It only merges top-level keys. So if parentObj has a key that over-laps with this.myObj, then sub-keys will be lost.
Object.assign({ a: { b: 2} }, { a: { c: 3 } }) // returns { a: { c: 3 } }
This is what I interpret your code as trying to do - though I am unfamiliar with vue.js at this time, so I cannot assure it will have the desired result to your webpage:
let value = "X";
let client = 1;
let index = 1;
const newObj = Object.assign({}, this.myObject);
// if you have lodash _.set is handy
newObj[client] = newObj[client] || {}; // whatever was there, or a new object
newObj[client][index] = value
this.myObject = newObj
Just use an array, thats reactive by design.
If you need to get elements from the array in your template or anywhere just add a find method
// temp
late
<div v-for="(value, idx) in myArray">{{find(obj => obj.id === idx)}}</div>
methods: {
find (searchFunction) {
return this.myArray.find(searchFunction)
}
}

Need to execute function when forEach functions ends

I have this code in node js / firebase :
ref.child("recipts").once("value", function(usersSnap) {
usersSnap.forEach(function(reciptsSnap) {
reciptsSnap.forEach(function(reciptSnap) {
reciptSnap.ref.child("last_recipt").once("value", function(b) {
b.forEach(function(c) { //Here I fill some "product" object
});
});
reciptSnap.forEach(function(b) { //Here I fill some "product" object
});
});
});
});
I need to execute a function just when "reciptSnap" forEachs finished. How can I accomplish this, I try using a variable i++ and i-- but only work for one forEach iteration.
The function I call is for manipulating the product object I created with the filled data from the forEachs loops.
If I have understood correctly, you want to call a function when reciptsSnap.forEach is complete and all async tasks inside it are also complete.
For achieving this, you can use the index parameter and the original array that is passed to the callback function of forEach. (See Documentation)
The code will be like this:
(Note: The following code is without changing the current forEach loop structure used. However, re-writing the code with Promise or async would be a better & cleaner way to do it).
var loop1Done = false;
var loop2Done = false;
ref.child("recipts").once("value", function (usersSnap) {
usersSnap.forEach(function (reciptsSnap) {
reciptsSnap.forEach(function (reciptSnap, index, colA) {
const idx = index;
const col = colA;
reciptSnap.ref.child("last_recipt").once("value", function (b) {
const i = idx;
const c = col;
b.forEach(function (c, j, colB) { //Here I fill some "product" object
// Do what you want here
// Check if all done for this loop
if ((j >= colB.length) && (i >= c.length)) {
loop1Done = true;
// Check if all loops done
if (loop1Done && loop2Done) {
// Call final callback function
// e.g. myFinalCallback();
}
}
});
});
reciptSnap.forEach(function (b, k, colC) { //Here I fill some "product" object
const i = idx;
const c = col;
// Do what you want here
// Check if all done for this loop
if ((k >= colC.length) && (i >= c.length)) {
loop2Done = true;
// Check if all loops done
if (loop1Done && loop2Done) {
// Call final callback function
// e.g. myFinalCallback();
}
}
});
});
});
});
Try:
reciptSnap.child("last_recipt").forEach(function(b) {
b.forEach(function(c) {
//Here I fill some "product" object
});
});
This should work since all of your data should already have been fetched when you did "value" on the receipts node.
If this works, your code is no longer asynchronous and right after the last forEach, you can execute the function you wanted to.
reciptSnap.forEach(function(b) {
//Here I fill some "product" object
});
//Execute your function here
});

Good way to handle Q Promises in Waterline with Sails.js

I have a problem that I'm importing some data and each new row depends on previous row being added (since each row has its order attribute set based on current maximum order from other objects). The flow is that I first try to find object with the same name, if not found I first check maximum order and create new object with order + 1 from that query.
I tried doing this with Q promises which are available under Waterline. I tried using all method as well as combining queries with then from Q docs:
var result = Q(initialVal);
funcs.forEach(function (f) {
result = result.then(f);
});
return result;
But all objects had the same order, just like they would be executed in parallel, instead of waiting for the first chain to finish.
I finally found a solution with recurrency, but I doubt it's the best way of working with promises. Here's the code that works (+ needs some refactor and cleaning etc.), to show the rough idea:
function findOrCreateGroup(groupsQuery, index, callback) {
var groupName = groupsQuery[index];
Group.findOne({ 'name' : groupName }).then(function(group) {
if (!group) {
return Group.find().limit(1).sort('order DESC').then(function(foundGroups) {
var maxOrder = 0;
if (foundGroups.length > 0) {
maxOrder = foundGroups[0].order;
}
return Group.create({
'name' : groupName,
'order' : (maxOrder + 1)
}).then(function(g) {
dbGroups[g.name] = g;
if (index + 1 < groupsQuery.length) {
findOrCreateGroup(groupsQuery, index + 1, callback);
} else {
callback();
}
return g;
});
});
} else {
dbGroups[group.name] = group;
if (index + 1 < groupsQuery.length) {
findOrCreateGroup(groupsQuery, index + 1, callback);
} else {
callback();
}
return group;
}
});
}

Resources