Javascript/jQuery: nesting loops, prevent overwriting object property - javascript

I have a function which should built an array with keys. Those keys will have a serie of objects as value.
I've some loops nested in each other and I'm quite near to the solution, but inside the last loop I'm making a mistake and I can't see the solution.
The function loopt over an array with Id's. Those will be the key value for the output array. After that it loops over an array with a lot of objects. Those objects are having a property 'category'. Some of them have one, others more. So with a for-loop I loop over all the categories.
If the categorie is the same as the id then it should push the object to the var objs, which will be added to the right key.
This is all working, BUT, I want that the objects are saved with only ONE category. So I declared a new var inside the last loop, put the obj inside there and set the obj.category. Unfortunately this is overwriting the 'source', array[x].category. This is not good because this occurs the problem that an object with two categories will only be found once in this function and it had to be found twice so it can be saved twice (once by every represent key value).
A lot of explanation for a little piece of code...
$.each(unique_id, function(i, el){
var objs = [];
for(var x in array)
{
for(var j=0; j<array[x].category.length; j++)
{
if(array[x].category[j] == el)
{
var obj = array[x];
obj.category = el;
objs.push(obj);
}
}
}
data[el] = objs;
})

What is happening is : both obj and array[x] are pointing to same object. they are two references that point to same object .Can you try below:
$.each(unique_id, function(i, el){
var objs = [];
for(var x in array)
{
for(var j=0; j<array[x].category.length; j++)
{
if(array[x].category[j] == el)
{
var obj = {};
$.extend(true,obj,array[x]);
obj.category = el;
objs.push(obj);
}
}
}
data[el] = objs;
});
Also, in javascript variables are function-scoped so even if you declare them inside the inner loop they are visible throughout the function and not only in the inner loop in which you have defined it. Of course $extend will copy every property that exists on array[x] and the nested objects and their properties as well. if you don't want that. just use
var obj = {};
obj.category = array[x].category;
provided category is also not object.

Related

Remove object from array if it also appears in JSON object

I am using this script to remove JSON objects from an array, which both appear within the array and another JSON object:
var stageChildren = stage.sprites;
for (var i = 0; i < stageChildren.length; i++) {
for (var x in mainMenu) {
if (mainMenu[x] === stageChildren[i]) {
console.log(x);
}
}
}
To make this more understandable, lets say I had two objects called: object1 & object2.
Inside object1, there may be the same JSON object which also appears within object2. If that's the case, the object is removed from object1.
While this script works, I think it might have a huge impact on performance. Why? Well, there's about 50 separate objects within stageChildren, and 10 inside mainMenu. The script loops through the first object inside stageChildren, checks if that object is also inside mainMenu (by performing a for loop again), and moves onto the next 49 objects.
Is there a more optimized way of doing this?
var index = 0;
var stageChildren = stage.sprites;
for (var x in mainMenu) {
if (stageChildren.includes(mainMenu[x])) {
const result = stageChildren.includes(mainMenu[x])
var index = stageChildren.indexOf(result);
stageChildren.splice(index, 1);
}
}

Sorting custom objects - couple of items always end up out of place

I am trying to sort a 2D array of custom objects, inside each inner array, based on one of the properties. This sub-arrays each represent one class, the outer array all the classes in the school. My strategy is as such:
Make a copy of the arry to provide a framework with the correct number of subarrays and indeces
Pass a copy of the sub-array to variable
Iterate over that array (the class) and pull out the last name from the object (which holds a number of other pieces of data on the child) and place it in an array that will be the index
Sort the index
Iterate over the class array, find the position of the last name in the index array, and insert the object into that index into the copied 'school'.
But this is not working. In some instances, one or two objects end up in the wrong place, in other instances it completely out of order. I have tried inspecting my index and comparing it with the 2D array, but the index is correct and I can't figure out why its not working. Here is the code:
var studentsInClass = // I have a function here that returns the 2D array of classes containing custom objects
var sortedStudentsInClass = studentsInClass;
var singleClassHolder = [];
var studentIndex = [];
// each iteration is for a single class
for(var i = 0; i < studentsInClass.length; i ++){
studentIndex = [];
singleClassHolder = studentsInClass[i];
// populate the student reference index
for(var j = 0; j < singleClassHolder.length; j++){
studentIndex.push(singleClassHolder[j].ID);
}
studentIndex.sort();
// iterate through students of single class, placing them in alphabetical order
for(var k = 0; k < singleClassHolder.length; k++){
sortedStudentsInClass[i][studentIndex.indexOf(singleClassHolder[k].ID)] = singleClassHolder[k];
}
}
return sortedStudentsInClass;
}
In case the object is important:
function Child(last, first, id, classroom, serviceDays, eligibility){
this.lastName = last;
this.firstName = first;
this.ID = id;
this.class = classroom;
this.maxServiceDays = serviceDays;
this.eligibility = eligibility;
}
And just a side note, it may seem extraneous having created the new singleClassHolder variable. After I noticed I did that, I removed it and just iterated through the 2D array, but that resulted in even more elements out of place.
Make a copy of the arry
var sortedStudentsInClass = studentsInClass;
This won't make a copy. It only makes one variable reference the other in memory. They both refer to the same array in memory. See related answer here.
The easiest way to fix the code is by declaring sortedStudentsInClass as a new array.
var studentsInClass = get2DArrayOfClasses();
var sortedStudentsInClass = [];
/*...*/
for(var k = 0; k < singleClassHolder.length; k++){
sortedStudentsInClass[i] = sortedStudentsInClass[i] || [];//declare inner array, if not present
sortedStudentsInClass[i][studentIndex.indexOf(singleClassHolder[k].ID)] = singleClassHolder[k];
}

Adding two same rows in array than modifying only one of them

First, I am adding two same rows in array and later I need to modify only the last one, adding new property to it. The way I do that:
for(var index in arrayOne) {
var arrayOneItem = arrayOne[index];
var new_row = {
address: arrayOne[index].address,
date: arrayOne[index].date,
category: arrayOne[index].category,
};
rows.push(new_row);
if(arrayOne[index].refund_status == 'refunded') {
rows.push(new_row);
rows[rows.length - 1].refund_status = 'refunded';
}
}
But the problem is that the code inside if statement does not only modify last row, but also the one before it, so the refund_status = 'refunded' is added both to the last and one before last row. Why is this happening and is there a way to modify the last row only?
When you are using the same object twice it's best to create a copy (shallow in this case) using Object.assign(). This will avoid referencing the same object from multiple variables or array indexes in your case.
eg.
rows.push(new_row);
becomes
rows.push(Object.assign({}, new_row));
This is because the object you push into the array is passed by reference and not by value, thus when you change the original object you will change both references to it in the array, see example below:
let someArray = [];
let someObj = {foo: "bar"};
someArray.push(someObj);
someArray.push(someObj);
someArray[0].foo = "baz";
console.log(someArray[1]);
To avoid this, you would need to clone the values of the object to create a new one. This question has some ways to do so, using JSON.parse and JSON.stringify is the shortest way to deep-copy an object without an external library, see example below:
let someArray = [];
let someObj = {foo: "bar"};
someArray.push(someObj);
let newObj = JSON.parse(JSON.stringify(someObj));
someArray.push(newObj);
someArray[0].foo = "baz";
console.log(someArray[1]);
Because you are changing property of an object, and object in javaScript is accessed through a link not a separate instance. In another words, you have the same object in memory and you change its property. It means, new_row is object you create and push it several times, and it's the same.
You can avoid it by copying it when pushing second times:
if(arrayOne[index].refund_status == 'refunded') {
rows.push({ ...new_row });
rows[rows.length - 1].refund_status = 'refunded';
}
where { ...new_row } basically creates new copy.
When you do rows[rows.length - 1].refund_status = 'refunded'; only second last will change.
Another solution i'd suggest is a bit more accurate:
const rows = []; // empty
const arrayOne = []; // SOME DATA HERE as I understand
const refundedStatus = ;
arrayOne.forEach(element=> {
rows.push(element);
if (value.refund_status === 'refunded') {
rows[rows.length].refund_status = 'refunded';
rows.push({ ...element});
}
});

JavaScript returning numeric value instead of string(s) after sorting array

I am working on an exercise where I prompt the user for a list of names, store the list of names in an array, sort the array in ascending order, and print the list of names (one per line). When I do so, I see a numeric value displayed instead of one name per line. Why is this happening?
var namesArray = [];
do {
var names = prompt("Enter a name: ");
namesArray.push(names);
} while (names != "")
namesArray.sort();
for (var name in namesArray) {
document.write(name);
}
When you use this construct:
for (var name in namesArray) {
the value of name will be the index in the array (the property name). If you want the actual value in the array, you have to use that property name/index to get the value:
document.write(namesArray[name]);
Of course, you really should not iterate arrays that way in the first place because that iterates all the enumerable properties of the array object (potentially including non array elements) as you can see in this example. Instead, you should use a traditional for loop as in this code example that follows:
var namesArray = [];
do {
var names = prompt("Enter a name: ");
namesArray.push(names);
} while (names != "")
namesArray.sort();
for (var i = 0; i < namesArray.length; i++) {
document.write(namesArray[i]);
}
Other options for iterating the array:
namesArray.forEach(function(value) {
document.write(value)
});
Or, in ES6, you can use the for/of syntax which does actually work how you were trying to use for/in:
for (let value of namesArray) {
document.write(value);
}
You also may want to understand that using document.write() after the document has already been parsed and loaded will cause the browser to clear the current document and start a new one. I don't know the larger context this code fits in, but that could cause you problems.
First, in a for..in loop, here name represents the keys and not the values in your array (you should use namesArray[name])
Also there is another important thing to note. An array is not recommended to be looped through using for..in and if so, you should do it like this:
for (var key in array) {
if (array.hasOwnProperty(key)) {
// then do stuff with array[key]
}
}
The usual preferred ways to loop through an array are the following:
A plain for loop
for (var i = 0, l = array.length; i < l; i++) {
// array[i]
}
Or a higher order function with Array.prototype.forEach (IE9+ if you need compat with IE)
array.forEach(function (item) {
// do something with the item
});

Removing an object from a javascript list of objects

I currently have a list of objects in javascript indexed by a key:
var list = [];
list['a'] = [];
list['a'].push({obj: 'test'});
list['a'].push({obj: 'test2'});
list['b'] = [];
list['b'].push({obj: 'test'});
list['b'].push({obj: 'test2'});
I would list to remove the entry based on the key (a/b)
I have tried the following:
for(var x in list) { delete list[x]; }
that works but it actually leaves an undefined entry in the list.
I have also tried splicing the array, but that does not seems to work in this case.
Any thoughts on how to remove the entry in javascript or jQuery?
Thanks.
The Fix:
After reading some of the comments, i was able to better understand what my list is consistent of. Therefor, i was able to do the removal by doing the following:
delete list.b;
I'm not sure if my list is best way to organize my structure, but doing a delete on the list and treating it like an object property did the trick.
Thanks for all the feedback.
I'll assume list is an object, not an array.
If you want to reset a or (or b it's done the same way)
list.a.length = 0;
If you want to delete an element from a at a known index (let index)
list.a.splice(index, 1);
You're attempting to add the elements to the array object as object properties and not as array elements. You can verify this by inspecting the value of list.length (will be 0).
So when doing something such as the following:
function removeProperty(id) {
if (list.hasOwnProperty(id)) {
delete list[id];
}
}
removeProperty('a');
it's really the same as:
delete list.a;
which is why you think it leaves an undefined 'entry' in the 'list'.
You'll need to use a literal object instead:
var list = {};
list['a'] = [];
...
list['b' = [];
...
which would allow you to use delete and have it behave as you expect. Of course you'll lose the .length property on the array but you never had that anyway.
Create a simple prototype for the Array class
Array.prototype.remove = function() {
// Helper function to remove a single element from a list if exists
item = arguments[0]
if (this.includes(item)) {
index = this.indexOf(item)
this.splice(index, 1)
}
}
// Now we can call
myList.remove(YourObject)
The above code will add the remove method to all your lists, so this will help you not just for objects but also strings, integers, or any data type
var list = {1: [{},{}], 2: [{},{}]};
function removeProperty(obj, prop){
if(obj[prop]){
delete obj[prop];
}
}
removeProperty(list,"1");
console.log(list);
If this quote:
I would list to remove the entry based on the key (a/b)
means you would like to select the list to consider based off the key (a/b), then remove elements in the list (or all of them), you can try this:
var list = [];
list['a'] = [];
list['a'].push({obj: 'test4'});
list['a'].push({obj: 'test5'});
list['b'] = [];
list['b'].push({obj: 'test'});
list['b'].push({obj: 'test2'});
var toRemove = 'test4';
var removeFrom = "a";
var consideredList;
for (var prop in list) {
if (prop == removeFrom) {
consideredList = list[prop];
}
}
//Remove everything from the considered list
consideredList.splice(0, consideredList.length);
//Remove based off value, if you know the property name
// for(var pos in consideredList) {
// if(consideredList[pos].obj == toRemove) {
// consideredList.splice(pos, 1);
// }
// }
I made a Plunker of a few different cases (check the script.js file). There seems to be a bit of confusion on what you are after and hopefully this is helpful to you somehow. Good luck.

Categories

Resources