Pushing to Array Overwrites Similar Entries - javascript

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.

Related

Javascript Nested Objects/Arrays Code Reduction Problem

I have an object for a collection of video data called "CollectionData". It has 3 key/value pairs:
collectionData.title - a simple string value
collectionData.seriesData - an array
collectionData.progressData - an array
Each of the 2 array values are objects:
collectionData.seriesData[x].title
collectionData.seriesData[x].year
collectionData.seriesData[x].length
collectionData.seriesData[x].synopsis
collectionData.progressData[x].currentTime
collectionData.progressData[x].progress
To save from having to type out collectionData.seriesData[x] or collectionData.progressData[x] every time to access the key's values I use a "pointer" into the object:
var p = collectionData.progressData[x];
I can then access the values of the keys by using p.currentTime, etc.
This works well if I want to replace a single key's value, i.e. p.currentTime = 25. However, if I want to replace the entire object's value, i.e. p = {currentTime:25,progress=10} the assignment operator does not evaluate the value of the variable p as a pointer into the object but as the pointer itself, therefore the object's value is not updated.
Is there a way to force the assignment operator to evaluate the variable p as a pointer or will I have to revert back to the full object name collectionData.progressData[x]={currentTime:25,progress=10}?
You could take Object.assign and the new data which update the object.
const
data = [{ currentTime: 1, progress: 1 }],
p = data[0];
Object.assign(p, { currentTime: 25, progress: 10 });
console.log(data);

How to get a JSON object's key from the object itself in JavaScript/a Vue v-for loop?

I'm not looking for the keys that this object contains but the key of the object itself (the key in the array containing the object).
I have this JSON:
{
"Object name (text)": {
"raw": "Some more text.",
},
"Another name": {
"raw": "Some other text.",
}
}
and would like to get "Object name (text)" for the first item.
My Vue code is:
<CustomComponent
v-for="object in objects"
:key="getKey(object)"
:object="object"
/>
I'm not sure if the getKey-method approach is how one is intended to get unique identifiers for iterating through the JSON array. Its code currently is:
getKey(object) {
return Object.keys(object)[0];
}
Now I'd like to somehow pass the name of the object to the CustomComponent ("Object name (text)" in the first case).
One temporary workaround that I intended to use until I find something more appropriate was getting the keys from the objects array like so:
:objectName="getObjectName(object)" and itemNumber: -1 in data and this method:
getObjectName(object) {
this.itemNumber = this.itemNumber + 1;
var objectName = Object.keys(this.objects)[this.itemNumber];
console.log("Object name: ", objectName);
}
However, the first line of this method causes it to run hundreds of times instead of only two times (why is that?; it works in the first 2 executions of the method and when commenting out that line) and I think this is unlikely the proper method to simply retrieve the object's name/key.
It also didn't work when putting the above code into the getKey method which would make more sense (and I had the code in that method before creating a separate method to debug). Then the key could be accessed in the component with this.$vnode.key However, it keeps being undefined. This might be a separate problem even though it could resolve this problem here as well - I might create a new question for it. It enters the methods "getKey" and "getObjectName" 6 times each even though it only renders two items on the page, like it should.
-> How to get the JSON object's key in JavaScript?
(Preferably from the object itself after iterating through a JSON array with a loop with Vue instead of only indirectly by checking the objects array.)
Edit: as a workaround I have now done this:
var keys = Object.keys(this.objects);
keys.forEach(element => {
this.objectsWithKeys.push({
object: this.objects[element],
key: element
});
});
<CustomComponent
v-for="objectWithKeys in objectsWithKeys"
:key="objectWithKeys.key"
:object="objectWithKeys.object"
>
</CustomComponent>
this.$vnode.key
This is solved, I used var objectsWithKeys = data[Object.keys(data)]; and {{ $vnode.key }}.

For loop always uses the last value JavaScript - VueJS

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 }

Multiple objects inside main object cannot be sorted by key value, but objects inside an array can be - how and why does this happen?

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);
}

Array of arrays and variable name based on object property

I call ajax service and I got back collection of objects. Object (A) properties are Group, order, salary. Let's say [Employee, 1, 1500],[Management, 2, 2000], [Employee, 3, salary] are my objects.
I need to to create an array for every group that can hold different objects (B), these objects could have format [OrderNo, String] and will be also retrieved by ajax call. If an array for a group was already created I do not want to call ajax for it again.
Problem is number of objects (A) or (B) isn't known in advance and I have to be able to reference the (A) object arrays and list thru them.
Result in pseudo code
var groups = [
Employee: [[1372, "Free meals"],[947, "Lower salary"],[21, "Overtimes"],[74667,"Great Xmass party"]],
Management: [[11, "Responsibility"],[485,"Extra meetings"]]
]
I don't know how to correctly write
for(var index in "Object A"){
if(groups.AlreadyContains("Object A[index]")){
do nothing;
}
else {
var "Object A[index]" = MyAjaxCall("Object A[index]");
groups.AddAnArray("Object A[index]");
}
}
function MyAjaxCall(par){ call ajax and return collection of B objects;}
foreach(var i in groups.Management)
{Console.log(Management[i].PropertyName);}
Console output: Responsibility, Extra meetings
You need to get both objects of type A and objects of type B ready and then start computation over them.
This is a common pattern in async functionality.
If you are using ES6
Promise.all(fetchObjectA, fetchObjectsB).then((objectsA, objectsB) =>
// Do things over both objectsA and B
)
If you are using jQuery
$.when(fetchObjectA, fetchObjectsB).done((objectsA, objectsB) =>
// Do same as above
)

Categories

Resources