as the title describes the problem, I'm using a for loop to extract elements of an array and asign it to a JSON value, it´s something like this:
hotel={ rooms: 2, price: [ 100, 200 ], occupation: [ '1 child', '1 adult' ]
and want to push into an array of JSON
hotels = [ { rooms:1, price: 100, occupation: '1 child' },... ]
so i tried this:
var json = { rooms : 1, price:null, occupation:null }
for (let i=0 ; i < hotel.rooms ; i++){
json.price = hotel.price[i]
json.occupation = hotel.occupation[i]
this.hotels.push(json)
}
but always the array hotels has the last values of the loop and (shows repeated the last value of the iteration), tried using the try {throw i} catch(ii) {...} but doesnt work
The problem is that each element of the array this.hotels is a reference to the same json object - so when it is mutated, anywhere, all those array elements "see" the change. Since you mutate json in each loop iteration, and simply overwrite the same keys, it's inevitable that each element ends up the same.
To fix it, simply push a new "copy" of the object - which will not therefore be affected by mutations of the "master" json object. There are several ways to do this, one being object spread notation:
this.hotels.push({...json});
You could also use Object.assign({}, json) instead of the spread notation. Or keep your code as it was, but move the var json = {...} inside the loop while replacing var with let - to ensure it's a new local variable each time, rather than one global that's continually mutated. Lots of solutions, as I said.
Object are reference types, you need to create a new object each time you push to the hotels array.
There are several ways to fix this two simple ways would be to:
Use object literals in the push method this.hotel.push({ rooms: 1, price: hotel.price[i], occupation: hotel.occupation[i]})
--OR--
Move the variable declaration into your loop var json = { rooms : 1, price:null, occupation:null }
Related
This is a document from my profiles collection:
{
_id: ObjectId("5f2ba3a43feccd0004b8698c")
userID: "238906554584137728",
serverID: "533691583845892100",
username: "W.M.K",
money: 15775,
__v: 4,
items: [...],
serverName: "W-15i: Overworld",
gladium: 7959.33,
stocks: [{
_id: {...},
stockID: "605b26d309a48348e05d88c9",
name: "GOOGL",
amount: 1,
desc: "Alphabet Inc's (Google) Stock."
}]
}
I'm using const profile = await Profile.findOne({userID: userID, 'stocks.stockName': stock.name})
EDIT: For one stock, it's as easy as profile.stocks[0].amount -=1. But when I have multiple stocks, how do I get the amount and manipulate it like amount -= 1?
there are few ways you can go through an array of objects in mongoose... you can map through the array, you can also use dot notation in an update. Either way one thing you'll need to do is mark the document modified because mongo doesn't detect changes in arrays of objects. To do this, make the changes and then use the markModified() function on the document. It takes a parameter of the array field name that was modified. in this case.... profile.markModified('stocks')
This is done after changes and before the save.
You can use dot notation with mongoose and you can also iterate through the array via map or forEach
The benefit of using map is that you can double it up with .filter() to filter the array for certain stocks
As already stated by Jeremy, it is basically a matter of array manipulations.
Here is one example:
for(let i = 0; i < profile.stocks.length; i++) {
profile.stocks[i].amount -= 1;
}
If you just want to update the profile on your database, you could also look into some very advanced mongodb querys (I think it's called "aggregation"), but that is probably overkill.
This is a follow up to a previous question I asked:
Sort JSON response by key value
So I know that objects cannot be sorted using the .sort method and if I push each object into an array, I can sort it the way I want.
Why does .sort not work on this:
{ A:{...}, B:{...}, C:{...} }
but works on this:
[ {...}, {...}, {...} ]
It's still accessing and working with object properties in both examples, right?
Here's some code:
var query = {
"736":{
ns: 42,
pageid: 12,
lang: "en",
index: 3
},
"421":{
ns: 12,
pageid: 36,
lang: "en",
index: 4
},
"102":{
ns: 2,
pageid: 19,
lang: "en",
index: 1
}
};
var queryArr = [{ns: 42, pageid: 12, lang: "en", index: 3}, {ns: 12, pageid: 36, lang: "en", index: 4}, {ns: 2, pageid: 19, lang: "en", index: 1}];
query is an object with multiple objects in it, and queryArr is an arry with multiple objects in it. If I wanted to sort query by its index key's value, I'd have to convert it to an arry first, and then run .sort on that array, correct?
What I want to know is why. What prevents query from being sorted, but allows the objects inside queryArr to be sorted? They're both still objects right? The only difference is the outer body is an object in the first, and an array in the second - but it's still working with objects when sorting.
My sort function still referneces the index using the object property accessor:
queryArr.sort(function(i,j){
return j.index - i.index;
});
Array object is object where the positive integer keys are used:
a = [, 1]
a[.1] = .1
a[-1] = -1
console.log( a )
console.log( { ...a } )
If the order of the properties can't be guaranteed, then for example array object [0, 1] can be stored as { "0": 0, "1": 1 } or { "1": 1, "0": 0 }.
Most array looping constructs will look for the "0" property before the "1" property, so the property order doesn't really matter.
Sorting array doesn't change it's properties order, but swaps the values associated with the properties.
Arrays are a special way of sequentially storing data. In the earliest implementations, this would be done by actually storing the array objects sequentially in memory. And resorting would actually physically move the objects in memory. So in your example where you have indices 102, 421, and 736. If you translated this to an array, you would actually have an array of length 737. 0 to 101 would be undefined, then you would have your object at 102. 103 to 420 would be undefined, then object 421. Et cetera.
Good to note that in your example when you translated your object into an array, you lost your keys (102, 421, 736). They simply became (0,1,2). In your example maybe this was okay, but if you had an object with properties like width, height, length, having these replaced with simple array indices like 0,1,2 would be a pretty significant loss of information.
Objects don't work the same way at all. They are not designed to store data sequentially, but hierarchically. So you can have a key 102 that points to an object, but you don't have to have keys 0-101. And key 102 doesn't designate the "order" of the item. It's just a name, and it could just as easily be length or fred or green as 102.
This mirrors reality. For example you might have an array to store a group of people, perhaps starting in order of age. And you could re-sort the list by different properties, like alphabetical by last name, or by height, etc.
But if we look at the objects within that array, it really makes no sense to talk about the order of firstName, lastName, height, weight, age, etc. There isn't really any "order" to speak of: weight doesn't have to come before height, or vice versa. And some people may like to see Last,First, while others prefer First Last. These properties are things we mostly want to be able to access directly by name, so an array isn't really ideal. We want named properties. Hence an object. Don't be confused just because you chose numbers as your property names...they're still names, not indices.
However it is of course possible to iterate all the properties of an object, and it is even possible to control the order in which you iterate them. Traditionally in javascript this iteration was done with the for...in syntax, which goes all the way back to es1. But you couldn't control the order in which the object's keys were iterated. To control order we would have to use for...in to populate an array of keys, then sort the array, then re-loop over the array.
However, it is possible in the newer es6 javascript world to iterate in some great new ways. For example, you can use Object.keys() to get an array of all the object keys (property names), and then you could sort this array. Saves the step of populating an array with for...in.
Another more advanced possibility in es6 (which uses Object.keys) is to actually make the object itself iterable by adding a Symbol.iterator. Again, this only works in es6+.
However the Symbol.iterator gives you a lot of power. This is what it looks like:
query[Symbol.iterator] = function*() {
let properties = Object.keys(this).sort();
for(let p of properties) {
yield {key:p, value:this[p]}
}
}
Once you add this iterator to the object, now you can use things like for...of. And in this example I added a .sort(), so this would iterate over the object's properties in ascending numeric order by key: 102, 421, 736. And since we are yielding an object with key and value, we are still able to see the key value, which we would NOT have if we had just translated to an array.
for(let {key,value} of query) {
console.log(key);
}
I have a handful of arrays one of first names, one of last names, and one of emails. I want to create an object for each index of the arrays.
firstnames[0], lastnames[0], emails[0]
would become
{firstname: value, lastname: value, email: value}
from which I would take that object and throw it in an array. However currently I am having trouble trying to figure out how to tackle this I can't wrap my brain around it. Hoping someone can help me come up with a clean method of doing this.
You just need a loop. On each iteration of the loop get the value from each array for the current index. A simple for loop would be easiest to understand.
The following uses the array .map() method to do this. That iterates over firstnames and builds a new array containing whatever values are returned by the function passed to .map() as an argument. The advantage of this is that you don't have to manually create an output array and push objects into it, .map() does that part for you, and also it avoids creating any working variables in the current scope.
This assumes all arrays are the same length.
var firstnames = ['Annie', 'Ben', 'Chris']
var lastnames = ['Andrews', 'Brown', 'Carmichael']
var emails = ['a#a.com', 'b#b.com', 'c#c.com']
var output = firstnames.map(function(v, i) {
return {
firstname: v,
lastname: lastnames[i],
email: emails[i]
}
})
console.log(output)
Let's say I have dynamically loaded object each time with different properties and an array of objects of that type:
var obj = {name: someValue, key: someValue2};
var objArray = [{name: someValue, key: someValue2},{name: someValue, key: someValue3}];
I want to find index of objArray which contains the obj. Some elements of objArray can have the same name property but different key property so searching through obj.name is not an option. So far I came up with this solution:
var keys = [];
_.forEach(Object.keys(obj), function(key) {
keys.push(key, obj[key])
});
var index = _.findIndex(objArray, keys);
This works fine and all but I am looking for something with better performance because this objArray can be very large.
So the question is: Is there a better way to find index of exact same object in object Array?
Update:
I forgot to mention that the names of the keys are not specified and can vary each time.
Use Array.prototype.findIndex(), this works only if you know in advance the property you want to check and hard code the rules in the callback for .findIndex().
An example:
var obj = {
name: 'someValue',
key: 'someValue3'
};
var objArray = [{
name: 'someValue',
key: 'someValue2'
}, {
name: 'someValue',
key: 'someValue3'
}];
var index = objArray.findIndex(function(item, index) {
return (item.name === obj.name) && (item.key === obj.key) ? true : false;
});
console.log('index is: ' + index);
This below it is another approach, basically it takes a JavaScript value (your initial object) and convert to a JSON string, and it uses that string to search within your array. The script works without any recursions, with any number of nested properties for your objects. The order of your property is important in this script, as the conversion to string take it in consideration.
Regarding "best way" is difficult to answer, depending what are your parameters for best way. If you consider performance, you should consider benchmarking your scripts and do test with some real data.
var obj = {
name: 'someValue',
key: 'someValue2'
},
objArray = [{
name: 'someValue',
key: 'someValue2'
}, {
name: 'someValue',
key: 'someValue3'
}];
var str = JSON.stringify(obj),
index = objArray.findIndex(function(item, index) {
return str === JSON.stringify(item) ;
});
console.log('index is: ' + index);
I can think of 2 approaches for doing this:
1.) What would speed up the process (especially if you got large array and large objects) is to create some kind of unique key for every object and map it to let's say property called: hash. If you wanna keep it vanilla, the best way to do that might be using the String.hashCode() method.
Iterate trough object OWN (check hasOwnProperty) properties and concat into single string both property name and then ### and then value and then %%% in between.
Then enhance your object with property hash like:
myObj.hash = String.hashCode(StringOfNamesAndValues);
Now iterate trough your array using for, and compare obj.hash to objArray[index].hash
Once they match you fond your object, store the index :)
If your object and one in the array don't have to be EXACTLY the same object but only a subset needs to be same. Instead using all property names in hash generation, use only the names and values of properties you wanna to be same. Compare hashes made in that way - voila!
2.) More brutish way would be to make object equal function that takes 2 objects and compares all the properties and values of respective properties. Now iterate trough array using that function. Pass your object and array object as parameters. If it returns that comparison is true, store index.
The bigger the objects are the slower this works.The bigger array is the slower this works. The more time you search for same objects slower this works (you compare every time instead making hash once).
Also, if you are having a very large set of object, and search often but add or remove to it sparsely, consider ordering the array in order considering hash values. Then use binary tree search for that hash to find appropriate object instead iterating from start to end each time.
Basically what I am trying to do is grab an object from one array, append a property to that object, and then push that new object to a new array. The problem is once I push that object to the new array (with the new, different property), it overwrites all previous objects in the new array that have similar properties to this new object, despite me altering the property before pushing.
What should be:
{propA: "Name", propB: "Age", propC: "Location1"}
{propA: "Name", propB: "Age", propC: "Location2"}
{propA: "Name", propB: "Age", propC: "Location3"}
Becomes:
{propA: "Name", propB: "Age", propC: "Location3"}
{propA: "Name", propB: "Age", propC: "Location3"}
{propA: "Name", propB: "Age", propC: "Location3"}
Here you can find the code, with the relevant lines highlighted in yellow: http://pastie.org/private/ym8dbp1jpjwyrgbpdubk3a#47-52,59,67,85-86
And here is the console output to show that the correct information is being pushed to cliAllow but are being overwritten: http://pastie.org/private/g3480a91tr3jvhftxaclq
Edit: Here is a simplified version in JSBin: http://jsbin.com/kanibo/2/edit?js,console
I am re-writing this code from my old code which was my first time writing in javascript so please excuse what may be incorrect programming methods elsewhere in the code.
Your help is greatly appreciated. I've been trying to debug this for almost a week and I am heavily exhausted.
The problem is that you are not pushing copies of the objects to the array cliAllow, but you're pushing references to the same object(s).
So let's go through the first iteration of the outer while loop to see what the result is:
cliAllow.push(arr[4]);
cliAllow.push(arr[4]);
cliAllow.push(arr[4]);
Now, cliAllow.length is 3, with indexes from 0 to 2, but you have 3 array items pointing to the very same object (the last one in the notPassed array, since you're counting backwards.)
So, cliAllow[0], cliAllow[1], cliAllow[2] all now references the same object.
That means, when you're setting the party property, you're change the same object 3 times:
cliAllow[2].party = "NT";
cliAllow[1].party = "VL";
cliAllow[0].party = "LF";
You're essentially doing this:
var obj = {milestone: "Mlstn4", client: "Client2", trade: "Trade3", units: "25.0", party: "B"};
obj.party = "NT";
obj.party = "VL";
obj.party = "LF";
As you can see, you're writing to the same property on the same object 3 times here, which means it will be set to the last value you give it ("LF" in this case).
In your code, you have 5 items in the notPassed array, but instead of 15 different objects, you have only 5, and each one of those 5 objects gets its party property set to the last value in the mlstnParties array.
One way of solving this is to make a copy function:
function copy(obj) {
var cp = {};
for (var o in obj) {
cp[o] = obj[o];
}
return cp;
}
See it in action here: http://jsbin.com/kibeba/2/edit
I'm writing this out in order to understand what the code is doing, and to confirm that I understand what it's doing.
When I ran the function named gatherResponses() it put 65 objects into the array notPassed (with the data you provided). The object properties are:
{client:"Client 1", trade:"Batch 21", units:250, milestone:"Milestone 18", due:(new Date(1423803600000))}
Then that array of objects, named notPassed gets passed to the function checkThresholds()
Then the elements (which are objects) of the notPassed array (now named arr) get pushed into another array named cliAllow.
cliAllow[cliAllow.length] = arr[arrLen];
Then a particular object in the new array gets accessed and the party property is assigned a value.
cliAllow[cliAllow.length-1].party = mlstnParties[0][mlstnCol];
The while loop is counting down from the biggest number to zero. Because of that, the variable arrLen is going from the highest number to the zero. This means that the array is being processed from the last element (last object) to the first object. It's not working "front to back", but backwards from the last to the first element (object) in the array.
In the original objects inside the notPassed array, there is no party property. So, it the party property that is getting added.
The party property is getting it's value from the mlstnParties array. That array is getting it's value from the spreadsheet.
If the party property isn't getting the correct values assigned to it, I'm wondering if there's a problem from the mlstnParties array? The mlstnParties array is a two dimensional array. The first dimension is each row, the second dimension is an array of all the values in that row.
This is the line of code I'm questioning:
cliAllow[cliAllow.length-1].party = mlstnParties[0][mlstnCol];
The row index is hard coded as zero. The code is never referencing any other row than whatever row got put into the first index of the array. I would think that you would want the first index parameter to be a variable that changes.