I have an nested array. Example
let array = [['a','e1'],['b','b1']]
What i want to achive is a new nested array with a copy of one of the array's and a change to that copy.
when i run it though a loop (tried for and foreach) it duplicates the change throughout the entire nested array.
here is an example of the code (note its not key: index and just an example. the actual inside array contains 11 values in total)
let array = [['a','e1'],['b','b1']]
let result = []
for(let x of array){
result.push(x);
if(x[1]==='e1'){
let newRow = x;
newRow[1] = 'e2'
result.push(newRow);
}
}
//result: [[a,e2],[a,e2],[b,b1]]
let needResult = [['a','e1'],['a','e2'],['b','b1']]
Any assistance in this would be greatly appreciated.
Working example of the script : https://stackblitz.com/edit/js-ah1bvf?file=index.js
Thanks
Instead of use let newRow = x;, as #tadman said in the comment you need to use another way like create a new array let newRow = []; //for example
let array = [['a','e1'],['b','b1'], ['c', 'e1']]
let result = []
for(let x of array){
result.push(x);
if(x[1]==='e1'){
const newRow = [];
newRow[0] = x[0];
newRow[1] = 'e2'
result.push(newRow);
}
}
console.log(result)
As #tadman said in the comment :
let newRow = [...x] // works.
I did not select #simons answer though valid as stated in my question the array can be quite large and I don't necessarily know exactly how long it is.
Thanks for the help!
I have an simple javascript array that may contain duplicates or maynot contain duplicates.
var names = [
['aaa','pin/test1.html'],
['bbb','pin/test2.html'],
['ttt','test.html'],
['ggg','test.html'],
['yyy','un/777.html'],
['ggg','test3.html'],
['nnn','test3.html'],
['eee','n/777.html'],
['sss','pin/test1.html'],
['xxx','pin/test2.html'],
['ppp','pin/test1.html'],
];
I need to find the duplicate filepath and put their name into new array. If there is no duplicate then assign its name in first and then assign '' after two values. I could point all the codes that I have tried but it doesnt work. I accept jquery solution also. The expected outcome is this.
var outcome = [
[['aaa','sss','ppp'], 'pin/test1.html'],
[['bbb','eee','xxx'], 'pin/test2.html'],
[['ttt','ggg',''], 'test.html'],
[['yyy','',''], 'un/777.html'],
[['ggg','nnn',''], 'test3.html'],
];
What I have tried is this
for (var i = 0; i < arr.length; i++) {
var uniqueNames = [];
$.each(arr[i], function (i, el) {
if ($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});
console.log(uniqueNames);
}
You could take a hash table and an array of empty strings and find the next slot for the value.
The array is reduced by taking an object as accumulator and a destructure array as value (the first part of the array) and key (the second part, aka filepath).
Inside of Array#reduce, a property check with the key is made and if undefined, an array with the wanted structure (array with two items, the first is an array with three emty spaces and the key) is being assigned by using a logical nullish assignment ??=.
The next line assigns the value to the next free slot, an item with an empty string.
Finally the accumulator is returned.
To get only an array as result, a conversion of the values of the object takes place.
let names = [['aaa','pin/test1.html'], ['bbb','pin/test2.html'], ['ttt','test.html'], ['ggg','test.html'], ['yyy','un/777.html'], ['ggg','test3.html'], ['nnn','test3.html'], ['eee','n/777.html'], ['sss','pin/test1.html'], ['xxx','pin/test2.html'], ['ppp','pin/test1.html']],
grouped = Object.values(names.reduce((r, [v, k]) => {
r[k] ??= [Array(3).fill(''), k];
r[k][0][r[k][0].indexOf('')] = v;
return r;
}, {}));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const aux = (names) => {
const hash = {};
let max = 0;
names.forEach(ele => {
if (!hash[ele[1]]) hash[ele[1]] = [];
hash[ele[1]].push(ele[0]);
max = Math.max(hash[ele[1]].length, max);
});
return Object.keys(hash).map(ele => [[...hash[ele], ...Array(max -hash[ele].length).fill("")], ele]);
}
var names = [
['aaa','pin/test1.html'],
['bbb','pin/test2.html'],
['ttt','test.html'],
['ggg','test.html'],
['yyy','un/777.html'],
['ggg','test3.html'],
['nnn','test3.html'],
['eee','n/777.html'],
['sss','pin/test1.html'],
['xxx','pin/test2.html'],
['ppp','pin/test1.html'],
];
console.log(aux(names))
This might help
You do not need jQuery for dealing with regular JS structure, you can achieve what you want with a simple code like this:
var names = [['aaa','pin/test1.html'],['bbb','pin/test2.html'],['ttt','test.html'],['ggg','test.html'],['yyy','un/777.html'],['ggg','test3.html'],['nnn','test3.html'],['eee','n/777.html'],['sss','pin/test1.html'],['xxx','pin/test2.html'],['ppp','pin/test1.html'],];
let lengthToFill = 0;
// collecting all the duplicates into a map
const pathMap = {};
names.forEach(name => {
// just in case if you're not familiar with array destructuring
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const [pathName, path] = name;
// make sure we have an array to deal with
// just in case you're not familiar with Nullish coalescing operator (??)
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
pathMap[path] = pathMap[path] ?? [];
pathMap[path].push(pathName);
// tracking the max number of elements we're adding into a single entry
lengthToFill = Math.max(lengthToFill, pathMap[path].length);
});
const result = Object.entries(pathMap).map(entry => {
// constructing new array entry based on the data we've collected so far
return [
entry[1].concat(Array(lengthToFill - entry[1].length).fill('')),
entry[0],
];
});
console.log(result);
This solution will work for any number of elements that you'd like to fill the array with ''. It makes sure that the length of final listing is the same for all entries.
I have a list that has objects with a varying amount of keys. I want to make sure that I get the index from the list of the object with the most keys OR the reference to the object itself. What is the best way to do this?
My current approach is:
let index = -1;
let numKeys = 0;
for(var i = 0; i < mylistofobjects.length; i++) {
if(Object.keys(mylistofobjects[i]).length > numKeys) {
index = i;
}
}
// by the end, index has the most keys
Is there a smarter/shorter way to do this that would require less code in this day and age? If the way to get the object reference is shorter than the way to get the index number.. I would prefer the object reference.
One option is to reduce, keeping in the accumulator the object with the most keys found so far:
const objWithMostKeys = mylistofobjects.reduce((bestSoFar, thisObj) => (
Object.keys(bestSoFar).length >= Object.keys(thisObj).length ? bestSoFar : thisObj
));
It's not entirely efficient because it checks the accumulator's number of keys on every iteration, rather than caching it, but caching it will require a bit more code:
let maxKeyCount = Object.keys(mylistofobjects[0]).length;
const objWithMostKeys = mylistofobjects.reduce((bestSoFar, currObj) => {
const currKeyCount = Object.keys(currObj).length;
if (currKeyCount > maxKeyCount) {
maxKeyCount = currKeyCount;
return currObj;
}
return bestSoFar;
});
This assumes that the mylistofobjects isn't empty. If that's a possibility, probably add a .length check beforehand, and return early / throw an error (or whatever you need to do) instead of proceeding.
Why is the logged Array always filled with data? Shouldnt it be an array with only one then two then three arrays in it?
var theArray=[];
function insertValues(species,quantity){
var w = window;
w[species]= [];
for(let i =0; i<quantity;i++){
w[species].push({
species:species,
randomValue:Math.random()*10
})
// console.log(theArray);
}
theArray.push(w[species]);
}
var listOfSpecies =[{animal:"Fish",amount:5},{animal:"Shark",amount:5},{animal:"Algae",amount:5}];
for(let i = 0; i<listOfSpecies.length; i++){
console.log(theArray);
insertValues(listOfSpecies[i].animal,listOfSpecies[i].amount);
}
Woah! Firstly, don't assign to window! (unexpected things will almost definitely occur).
Also, JavaScript objects (yes an array is an object, typeof [] === "object" // true) are passed by reference, not by value.
When you add to theArray, a new reference is created. When you go to log it to the console, it shows an empty array at first, but it has actually logged a reference to theArray, therefore, when you go to inspect the contents, it shows an array filled with values;
Even try the example below, the same thing occurs (albeit much simpler to follow)
var arr = [];
for (var idx = 0; idx < 3; idx++) {
console.log(arr);
arr[idx] = idx;
}
to prevent this, you would need to copy the array, like so:
var newArray = Object.assign([], theArray);
Object.assign copies the values of the array (or object), returning a new array (again, or object), but does not create a reference back to the original array or object.
In the following code sample i get a strange behavior
var data = ['xxx', 'yyy'];
for (var i in data)
{
var a = i;
var b = data[i];
}
The two first iterations works just fine. I get index "0" and "1" in i, but then it loops one extra time and now the i is "sum". Is this by design or what is this extra iteration used for? The result in my case is always empty and it messes up my code. Is there a way to not do his extra loop?
BR
Andreas
It looks like you (or some other code you've included) have added extra properties onto the Array prototype. What you should be doing is checking to see whether the object you're iterating over actually has that property on itself, not on its prototype:
for (i in data) {
if (data.hasOwnProperty(i)) {
a = i;
b = data[i];
}
}
That said, you should never use for .. in on arrays. Use a regular for loop.
See here for more information: http://yuiblog.com/blog/2006/09/26/for-in-intrigue/
You are looping through an Array, not through an Object. For arrays it's better to use:
for (var i=0; i<data.length; i=i+1){
/* ... */
}
In your loop every property of the Array object is taken into account. That makes the for ... in loop for array less predictable. In your case it looks like sum is a property (method) that's added to Array.prototype elsewhere in your code.
There are more ways to loop through arrays. See for example this SO-question, or this one
Just for fun, a more esoteric way to loop an array:
Array.prototype.loop = function(fn){
var t = this;
return (function loop(fn,i){
return i ? loop(fn,i-1).concat(fn(t[i-1])) : [];
}(fn,t.length));
}
//e.g.
//add 1 to every value
var a = [1,2,3,4,5].loop(function(val){return val+1;});
alert(a); //=> [2,3,4,5,6]
//show every value in console
var b = [1,2,3,4,5].loop(function(val){return console.log(val), val;});
Here's a way to safely iterate.
var data = ['xxx', 'yyy'];
for (var i = 0; i < data.length; i++)
{
var a = i;
var b = data[i];
}
What you are getting is an method coming from extending the Array object, I guess you are using some library where is something like
Array.prototype.sum = function () {...};
Perhaps setting data like this would work better: var data = {0:'xxx', 1:'yyy'};
First of all data is an object. Try to add console.log(a); and console.log(b); inside your loop and you'll see.