Lodash - Using _.update inside _.forEach - scope

I'm trying to use the _.update method inside of the _.forEach method and I'm not understanding why the newly added property has a value of undefined.
FYI, I have to have the function defined separately and then pass it to the _.update method, I can't write it directly into the _.update method (it must be dynamic).
I've tried structuring this in multiple different ways, but none of them work.
let object = [{ 'a': 1, 'b': 1 }, { 'a':1, 'b': 1 }]
function myFunc (row) { return row.a + row.b }
_.forEach(object, row => _.update(row, 'c', myFunc(row)))
console.log(object)
I expected to get:
[{ 'a': 1, 'b': 1, 'c': 2 }, { 'a':1, 'b': 1, 'c': 2 }]

The _.update() method is used for updating an existing property, and it accepts an updater function, and not a value (like the one generated by myFunc).
In your case you should use _.set(), that accepts a value:
const object = [{ 'a': 1, 'b': 1 }, { 'a':1, 'b': 1 }]
function myFunc(row) {
return row.a + row.b
}
_.forEach(object, row => _.set(row, 'c', myFunc(row)))
console.log(object)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
And if you don't have to use lodash, you can use Array.forEach():
const object = [{ 'a': 1, 'b': 1 }, { 'a':1, 'b': 1 }]
object.forEach(row => row.c = myFunc(row))
function myFunc(row) {
return row.a + row.b
}
console.log(object)

Related

Mongoose push item to array if not exists

I have a model schema like this:
Collection: {
irrelevant fields,
object: {
more irrelevant fields,
array:
[
{
field1,
field2,
_id: false
}
]
}
}
I want to push to array any object {field1, field2} that does not repeat with field1 already existing in array (in an updateOne query).
field2 is constantly updating itself and i have tried addtoset but it considers field2 so it ends up adding another item i don't want to add.
For example:
array:[
{field1: 1, field2: 6},
{field1: 2, field2: 4},
{field1: 3, field2: 1}
]
if i would want to push {field1: 2, field2: 6} it should not let me push it because the field1:2 already exists in array.
(using addtoset, even it does check that the field1:2 already existis, it ends up adding te object because of field2 being different)
array:[
{field1: 1, field2: 6},
{field1: 2, field2: 4},
{field1: 3, field2: 1}
{field1: 2, field2: 6}
]
You could use the pipeline form of update with the $cond operator. If the field1 value already exists, keep the value of the array the same, if not, append the new value to the end.
Perhaps something similar to:
const newValue = {field1: 2, field2: 6};
db.collection.update({match criteria},
[{$set:{
array:{
$cond:{
if: {$in: [newValue.field1, "$array.field1"]},
then: "$array",
else: {$concatArrays: ["$array", [newValue]]}]}
}
}
}}]
)

Could I parse to `json/[object] using xlsx-populate

I want to parse an array of objects into xlsx-populate so it can give me the excel file.
const xlsxPopulate = require('xlsx-populate');
const worksheet = await xlsxPopulate.fromBlankAsync();
const sheet1 = worksheet.sheet('Sheet1');
sheet1.cell('A1').value(newArray);
await worksheet.toFileAsync('./myFileName.xlsx');
It works for Arrays or Arrays([[..][..]]) using range or using single cell as well.
From the doc:
xlsx-populate also supports ranges of cells to allow
parsing/manipulation of multiple cells at once.
const r = workbook.sheet(0).range("A1:C3");
// Set all cell values to the same value:
r.value(5);
// Set the values using a 2D array:
r.value([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
// Set the values using a callback function:
r.value((cell, ri, ci, range) => Math.random());
Alternatively, you can set the values in a range with only the
top-left cell in the range:
workbook.sheet(0).cell("A1").value([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
So all you need to do is converting objects to CSV maybe for an example JS object:
Modified(only removed the .joins) From this answer:
const json = [
{
h1:1,
h2:2
},
{
h1:3,
h2:4
}
];
var fields = Object.keys(json[0])
var replacer = function(key, value) { return value === null ? '' : value }
var csv = json.map(function(row){
return fields.map(function(fieldName){
return JSON.stringify(row[fieldName], replacer)
})
})
csv.unshift(fields) // add header column
console.log(csv) //[ [ 'h1', 'h2' ], [ '1', '2' ], [ '3', '4' ] ]

Trying to get API's Promise resolved using map()

UPDATE: Solved!
The only change needed was making the await of the API call to a return, then the map was returned with both image names and ids. Still trying to get the map converted to an array, but this question for this post is solved. Here's the new printout:
1 face detected from image Family1-Son1.jpg with ID 98091e1e-bc8d-4c93-a850-a115684a6e6e
Family1-Son1.jpg
1 face detected from image Family1-Dad3.jpg with ID f94360f5-feb3-4d14-816f-7d854fc0b34c
Family1-Dad3.jpg
[ { '0': 'F',
'1': 'a',
'2': 'm',
'3': 'i',
'4': 'l',
'5': 'y',
'6': '1',
'7': '-',
'8': 'D',
'9': 'a',
'10': 'd',
'11': '3',
'12': '.',
'13': 'j',
'14': 'p',
'15': 'g',
id: 'f94360f5-feb3-4d14-816f-7d854fc0b34c' },
{ '0': 'F',
'1': 'a',
'2': 'm',
'3': 'i',
'4': 'l',
'5': 'y',
'6': '1',
'7': '-',
'8': 'S',
'9': 'o',
'10': 'n',
'11': '1',
'12': '.',
'13': 'j',
'14': 'p',
'15': 'g',
id: '98091e1e-bc8d-4c93-a850-a115684a6e6e' } ]
[ <2 empty items> ]
[]
I've tried many different methods on here, can't get them to work. This one is the closest I came. I am trying to call an API but on each item in an array. I can't do this in a regular loop, so many reasons why not. So someone said use the array.map() function instead of a loop. I got this far:
const IMAGE_BASE_URL = 'https://csdx.blob.core.windows.net/resources/Face/Images/'
let sourceImageFileNames = ['Family1-Dad3.jpg', 'Family1-Son1.jpg']
// Detect faces in the source image array, then get their IDs
let sourcefaceMap = await Promise.all(sourceImageFileNames.map(async (imageName) => {
// Returns a Promise<DetectedFace[]>
await client.face.detectWithUrl(IMAGE_BASE_URL + imageName)
.then((faces) => {
console.log(`${faces.length} face detected from image ${imageName} with ID ${faces[0].faceId}`)
let id = faces[0].faceId
return { ...imageName, id }
}).catch((err) => {
console.log(`No face detected in: ${sourceImageFileNames[0]}.`)
throw err;
})
}))
let values = Object.values(sourcefaceMap)
console.log(values)
// Create an array to store the source face IDs
var sourceFaceIds = new Array(sourceImageFileNames.length)
console.log(sourceFaceIds)
let ids = sourceFaceIds.filter((id)=> {
return id != null;
})
console.log(ids)
The values seem to be there in when debugging, but then when I try to return the map, it prints out as undefined. Even though the map does do the job of looping to get each id (as seen in the top two print statements).
Here is my printout:
VERIFY
1 face(s) detected from image Family1-Dad3.jpg with ID f94360f5-feb3-4d14-816f-7d854fc0b34c
1 face(s) detected from image Family1-Son1.jpg with ID 98091e1e-bc8d-4c93-a850-a115684a6e6e
[ undefined, undefined ]
[ <2 empty items> ]
[ <2 empty items> ]
Here is a screenshot of the id having value, when I hover over it in the return statement:
Basically, I am trying to do an API call with URL images, then the API will associate an ID with each face in the image. I need the API call to return all those IDs. In this case, it's only 2 IDs. How do I do this? At the end of my code, I just need an array of those IDs. That's all. Thanks.
Your map callback didn't return anything, it only waited. Use
const values = await Promise.all(sourceImageFileNames.map(imageName => {
// now *really* returns a Promise<DetectedFace[]>
return client.face.detectWithUrl(IMAGE_BASE_URL + imageName)
.then(faces => {
console.log(`${faces.length} face detected from image ${imageName} with ID ${faces[0].faceId}`)
let id = faces[0].faceId
return { imageName, id }
}).catch((err) => {
console.log(`No face detected in: ${sourceImageFileNames[0]}.`)
throw err;
})
}));
console.log(values);
I'm also pretty certain that you didn't want to spread the imageName string into an object, and don't need to call Object.values on an array to get its values as an array.
I think the problem you are facing here on line let id = response.faceId.
id is getting its value from response.faceId please do check that faceId property present in the response object. If it is present then code will work as you expected.

NodeJS and MongoDB update not working

I have this loop in a Node and Mongo application. console.log will output all the 'product' names correctly, however, rank is usually not set correctly in the database. Interestingly enough, if I put a debug breakpoint in the program and step through slowly, it works. Any idea if there is some sort of race condition taking place, and what the best solution for making this work would be?
async.each(sortedProductsArray, function (product, callback) {
console.log(product.name);
var query = {
productNo: product.productNo
};
var index = sortedProductsArray.indexOf(product)+1;
var update = {
// give it a rank
$set: { 'rank': index }
};
// main products array
products.update(query, update, callback);
});
you should probably be using forEachOf and this will return the index as the second parameter, e.g.:
async.forEachOf(['a', 'b', 'c'], function () {console.log(arguments)});
{ '0': 'a', '1': 0, '2': [Function] }
{ '0': 'b', '1': 1, '2': [Function] }
{ '0': 'c', '1': 2, '2': [Function] }
and then you should be using this index rather than doing another search over your products collection
edit:
when you call products.update(query, update, callback); this affects the products array, and therefore may not be valid when you do your indexOf() (may not exist) which is why the rank sometimes isnt populated

Running reduce function twice on an array ? results in an undefined node js

I am having issues with node js. I am thinking this has to do with how the code runs async.
Is it because that the reduce function is running asynchronously ?
If so what is the idiomatic pattern I must follow inorder to run these function one after another?
I am trying to run reduce operation twice and the second time I am getting an undefined
function hello () {
var values = [
{"a": 1},
{"b": 2},
{"c": 3},
{"d": 4},
{"b": 5},
{"a": 6},
{"a": 7},
{"b": 8},
{"c": 9},
{"d": 10}
];
var kvList = values.reduce(function (result, element) {
var key = Object.keys(element);
var val = element[key];
// if undefined create an array.
result[key] = result[key] || [];
result[key].push(val);
return result;
}, []);
console.log(kvList);
console.log(kvList[0]); // becomes undefined here.
var pairs = kvList.reduce(function (result, element) {
console.log("HELLO");
var key = Object.keys(element)[0];
console.log(element);
var subArray = element[key];
var total = subArray.reduce(function (result, element) {
return result + element;
}, 0);
result[key] = total;
return result;
}, []);
console.log(pairs);
};
hello();
Array.prototype.reduce() does not run asynchronously.
The issue is that .reduce() operates on array elements (sequentially numbered elements that are accessed via a numeric index starting at 0). Your first array has array elements. The result of your first .reduce() however is an array object, but has only properties such as 'a', 'b', 'c' and and no actual array elements. For example, after your first .reduce() operation, kvList.length === 0 so the second .reduce() does not cycle through the elements like you want it to.
The culprit in the first .reduce() loop are these lines of code:
// if undefined create an array.
result[key] = result[key] || [];
result[key].push(val);
This adds a property to result with the property name key. It does not actually add an element to the array so the second .reduce() then has no array elements to operate on.
OK, now that you've shown the desired result, this generates that result:
var values = [
{"a": 1}, {"b": 2}, {"c": 3}, {"d": 4}, {"b": 5},
{"a": 6}, {"a": 7}, {"b": 8}, {"c": 9}, {"d": 10}
];
var dict = {};
values.forEach(function(obj) {
// iterate the properties of each object in the array
for (var prop in obj) {
// if not already in the dict, add an empty array to the dict for this property name
if (!(prop in dict)) {
dict[prop] = [];
}
// add this number to the array for this dict entry
dict[prop].push(obj[prop]);
}
});
// dict is {"a":[1,6,7],"b":[2,5,8],"c":[3,9],"d":[4,10]}
// process back into an array
var result = [];
// iterate each property in the dict
for (var prop in dict) {
// create an object that is of this form {prop: [val1, val2, val3]}
// and put it in the result array
var temp = {};
temp[prop] = dict[prop];
result.push(temp);
}
// result is [{"a":[1,6,7]},{"b":[2,5,8]},{"c":[3,9]},{"d":[4,10]}]
Working demo: http://jsfiddle.net/jfriend00/q8u208y4/
FYI, your data structures of an object with a single key are somewhat weird and hard to work with. I'm wondering why the final result isn't what's in dict which is actually easier to work with in code since you can just iterate the properties directly to get each property and corresponding array.

Resources