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
Related
Given this map
r = {
'items': [
{
'id': '1',
'name': 'foo'
},
{
'id': '2',
'name': 'bar'
}
]
}
I am trying to get the 'id' for 'name'=='foo'. I have this:
Id = [api['id'] for api in r['items'] if 'foo' in api['name']]
But then Id == ['1']. I want it to = "1". I can do this:
Id = [api['id'] for api in r['items'] if 'foo' in api['name']][0]
But that seems like a workaround. Is there a way to write that in such a way as to pass only the value of api['id'] rather than the value within a list?
You can use a generator:
Id = next(api['id'] for api in r['items'] if api['name'] == 'foo')
The added benefit is that the iteration will be stopped as soon as you encounter matching object, whereas your original code would process all of the original list and create a new one, only to extract its first element.
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.
This is my query
var db = firebase.database();
var ref = db.ref('Users');
ref.on('child_added', function(snapshot){
console.log(snapshot.val())
});
and this is out put
{ callCount: 1,
favoriteCount: 1,
interest: [ 'dogs', 'cats', 'technology' ],
tickCount: 1 }
{ callCount: 15,
favoriteCount: 7,
interest: [ 'Technology', 'Azure', 'Asp.NET MVC', 'JavaScript' ],
tickCount: 3 }
I want to add a array and show it like this
[{ callCount: 1,
favoriteCount: 1,
interest: [ 'dogs', 'cats', 'technology' ],
tickCount: 1 },
{ callCount: 15,
favoriteCount: 7,
interest: [ 'Technology', 'Azure', 'Asp.NET MVC', 'JavaScript' ],
tickCount: 3 }]
How can I do ?
This will not be possible with child_added event, because it never gives any indication about reaching the end of the dataset, and will continue to emit new items when they are added.
Instead, you should subscribe to the value which will emit snapshot of the full tree in the beginning and whenever anything changes.
Since you only need to get the value once, here's how you do it:
const db = firebase.database();
const ref = db.ref('Users');
ref.once('value', snapshot => {
const result = [];
snapshot.forEach(item => {
result.push(item.val());
return false;
});
// Do something with the result
});
A few things to note here:
We are subscribing to event using once since we are only interested in the initial value. If you want to react to changes in dataset somehow, you may use on instead.
We are using forEach to get to the snapshot value, instead of val because we care about the item order. forEach guarantees that the callback will be invoked with all of the snapshot keys in order, as specified by orderBy query. val on the other hand, would return an object with unreliable ordering of the keys.
forEach will stop as soon as the callback returns true, which we don't want to happen. Returning nothing will not cancel enumeration, but it's better to be explicit in case someone decides to be concise and removes the curly braces.
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.
I'm using NodeJS 0.10.13. I'm just curious about the behavior of the following code snippet:
> var a = ['1','2','3']
undefined
> a.map(function(){return path.resolve(arguments[0])})
[ '/Users/user/1',
'/Users/user/2',
'/Users/user/3' ]
> a.map(path.resolve)
TypeError: Arguments to path.resolve must be strings
at exports.resolve (path.js:313:15)
> a.map(path.resolve.bind(path)))
TypeError: Arguments to path.resolve must be strings
at exports.resolve (path.js:313:15)
Why is it that the 2nd and 3rd map calls return an error when the array only has strings? Going to the relevant line in NodeJS's source code yields this:
if (typeof path !== 'string') {
throw new TypeError('Arguments to path.resolve must be strings');
} else if (!path) {
continue;
}
Which makes no sense as to why the arguments are not strings. Does anyone have any clue?
The callback to Array.prototype.map is passed three arguments: current element, index, and the array being traversed.
a.map(path.resolve);
a.map now calls path.resolve using a construct similar to this:
path.resolve.call(undefined, element, index, array);
path.resolve([from ...], to) can accept var args. If you go through the source of path.js
for (var i = arguments.length - 1; i >= -1; i--) {
//..irrelevant lines
var path = arguments[i];
if (typeof path !== 'string') {
throw new TypeError('Arguments to path.resolve must be strings');}
else if (!path) {
continue;
}
}
In the first iteration, path is the third argument, which is an Array.
typeof arrayObject !== 'string' evaluates to true and hence the TypeError
This happens because the parameters passed to each call of the mapped function will get not only the actual elment, but also the array index and the whole array.
To see exactly what parameters gets sent to map, try this:
> var a = ['1', '2', '3'];
['1', '2', '3']
> a.map(function() { return arguments});
[ { '0': '1',
'1': 0,
'2': [ '1', '2', '3' ] },
{ '0': '2',
'1': 1,
'2': [ '1', '2', '3' ] },
{ '0': '3',
'1': 2,
'2': [ '1', '2', '3' ] } ]
Since the object sent to the mapped function (path.resolve in this case) is not a string but an object, you get a TypeError.