merge two arrays (keys and values) into an object [duplicate] - javascript

This question already has answers here:
Merge keys array and values array into an object in JavaScript
(14 answers)
Closed 9 years ago.
Is there a common Javascript/Coffeescript-specific idiom I can use to accomplish this? Mainly out of curiosity.
I have two arrays, one consisting of the desired keys and the other one consisting of the desired values, and I want to merge this in to an object.
keys = ['one', 'two', 'three']
values = ['a', 'b', 'c']

var r = {},
i,
keys = ['one', 'two', 'three'],
values = ['a', 'b', 'c'];
for (let i = 0; i < keys.length; i++) {
r[keys[i]] = values[i];
}
console.log(r);
.as-console-wrapper { max-height: 100% !important; top: 0; }

keys = ['one', 'two', 'three']
values = ['a', 'b', 'c']
d = {}
for i, index in keys
d[i] = values[index]
Explanation:
In coffeescript you can iterate an array and get each item and its position on the array, or index.
So you can then use this index to assign keys and values to a new object.

As long as the two arrays are the same length, you can do this:
var hash = {};
var keys = ['one', 'two', 'three']
var values = ['a', 'b', 'c']
for (var i = 0; i < keys.length; i++)
hash[keys[i]] = values[i];
console.log(hash['one'])
console.log(hash.two);

Related

Are there any differences between spreading two arrays in a new array vs calling `[].concat`? [duplicate]

What is the difference between spread operator and array.concat()
let parts = ['four', 'five'];
let numbers = ['one', 'two', 'three'];
console.log([...numbers, ...parts]);
Array.concat() function
let parts = ['four', 'five'];
let numbers = ['one', 'two', 'three'];
console.log(numbers.concat(parts));
Both results are same. So, what kind of scenarios we want to use them? And which one is best for performance?
concat and spreads are very different when the argument is not an array.
When the argument is not an array, concat adds it as a whole, while ... tries to iterate it and fails if it can't. Consider:
a = [1, 2, 3]
x = 'hello';
console.log(a.concat(x)); // [ 1, 2, 3, 'hello' ]
console.log([...a, ...x]); // [ 1, 2, 3, 'h', 'e', 'l', 'l', 'o' ]
Here, concat treats the string atomically, while ... uses its default iterator, char-by-char.
Another example:
x = 99;
console.log(a.concat(x)); // [1, 2, 3, 99]
console.log([...a, ...x]); // TypeError: x is not iterable
Again, for concat the number is an atom, ... tries to iterate it and fails.
Finally:
function* gen() { yield *'abc' }
console.log(a.concat(gen())); // [ 1, 2, 3, Object [Generator] {} ]
console.log([...a, ...gen()]); // [ 1, 2, 3, 'a', 'b', 'c' ]
concat makes no attempt to iterate the generator and appends it as a whole, while ... nicely fetches all values from it.
To sum it up, when your arguments are possibly non-arrays, the choice between concat and ... depends on whether you want them to be iterated.
The above describes the default behaviour of concat, however, ES6 provides a way to override it with Symbol.isConcatSpreadable. By default, this symbol is true for arrays, and false for everything else. Setting it to true tells concat to iterate the argument, just like ... does:
str = 'hello'
console.log([1,2,3].concat(str)) // [1,2,3, 'hello']
str = new String('hello');
str[Symbol.isConcatSpreadable] = true;
console.log([1,2,3].concat(str)) // [ 1, 2, 3, 'h', 'e', 'l', 'l', 'o' ]
Performance-wise concat is faster, probably because it can benefit from array-specific optimizations, while ... has to conform to the common iteration protocol. Timings:
let big = (new Array(1e5)).fill(99);
let i, x;
console.time('concat-big');
for(i = 0; i < 1e2; i++) x = [].concat(big)
console.timeEnd('concat-big');
console.time('spread-big');
for(i = 0; i < 1e2; i++) x = [...big]
console.timeEnd('spread-big');
let a = (new Array(1e3)).fill(99);
let b = (new Array(1e3)).fill(99);
let c = (new Array(1e3)).fill(99);
let d = (new Array(1e3)).fill(99);
console.time('concat-many');
for(i = 0; i < 1e2; i++) x = [1,2,3].concat(a, b, c, d)
console.timeEnd('concat-many');
console.time('spread-many');
for(i = 0; i < 1e2; i++) x = [1,2,3, ...a, ...b, ...c, ...d]
console.timeEnd('spread-many');
Well console.log(['one', 'two', 'three', 'four', 'five']) has the same result as well, so why use either here? :P
In general you would use concat when you have two (or more) arrays from arbitrary sources, and you would use the spread syntax in the array literal if the additional elements that are always part of the array are known before. So if you would have an array literal with concat in your code, just go for spread syntax, and just use concat otherwise:
[...a, ...b] // bad :-(
a.concat(b) // good :-)
[x, y].concat(a) // bad :-(
[x, y, ...a] // good :-)
Also the two alternatives behave quite differently when dealing with non-array values.
I am replying just to the performance question since there are already good answers regarding the scenarios. I wrote a test and executed it on the most recent browsers. Below the results and the code.
/*
* Performance results.
* Browser Spread syntax concat method
* --------------------------------------------------
* Chrome 75 626.43ms 235.13ms
* Firefox 68 928.40ms 821.30ms
* Safari 12 165.44ms 152.04ms
* Edge 18 1784.72ms 703.41ms
* Opera 62 590.10ms 213.45ms
* --------------------------------------------------
*/
Below the code I wrote and used.
const array1 = [];
const array2 = [];
const mergeCount = 50;
let spreadTime = 0;
let concatTime = 0;
// Used to popolate the arrays to merge with 10.000.000 elements.
for (let i = 0; i < 10000000; ++i) {
array1.push(i);
array2.push(i);
}
// The spread syntax performance test.
for (let i = 0; i < mergeCount; ++i) {
const startTime = performance.now();
const array3 = [ ...array1, ...array2 ];
spreadTime += performance.now() - startTime;
}
// The concat performance test.
for (let i = 0; i < mergeCount; ++i) {
const startTime = performance.now();
const array3 = array1.concat(array2);
concatTime += performance.now() - startTime;
}
console.log(spreadTime / mergeCount);
console.log(concatTime / mergeCount);
The one difference I think is valid is that using spread operator for large array size will give you error of Maximum call stack size exceeded which you can avoid using the concat operator.
var someArray = new Array(600000);
var newArray = [];
var tempArray = [];
someArray.fill("foo");
try {
newArray.push(...someArray);
} catch (e) {
console.log("Using spread operator:", e.message)
}
tempArray = newArray.concat(someArray);
console.log("Using concat function:", tempArray.length)
There is one very important difference between concat and push in that the former does not mutate the underlying array, requiring you to assign the result to the same or different array:
let things = ['a', 'b', 'c'];
let moreThings = ['d', 'e'];
things.concat(moreThings);
console.log(things); // [ 'a', 'b', 'c' ]
things.push(...moreThings);
console.log(things); // [ 'a', 'b', 'c', 'd', 'e' ]
I've seen bugs caused by the assumption that concat changes the array (talking for a friend ;).
Update:
Concat is now always faster than spread. The following benchmark shows both small and large-size arrays being joined: https://jsbench.me/nyla6xchf4/1
// preparation
const a = Array.from({length: 1000}).map((_, i)=>`${i}`);
const b = Array.from({length: 2000}).map((_, i)=>`${i}`);
const aSmall = ['a', 'b', 'c', 'd'];
const bSmall = ['e', 'f', 'g', 'h', 'i'];
const c = [...a, ...b];
// vs
const c = a.concat(b);
const c = [...aSmall, ...bSmall];
// vs
const c = aSmall.concat(bSmall)
Previous:
Although some of the replies are correct when it comes to performance on big arrays, the performance is quite different when you are dealing with small arrays.
You can check the results for yourself at https://jsperf.com/spread-vs-concat-size-agnostic.
As you can see, spread is 50% faster for smaller arrays, while concat is multiple times faster on large arrays.
The answer by #georg was helpful to see the comparison. I was also curious about how .flat() would compare in the running and it was by far the worst. Don't use .flat() if speed is a priority. (Something I wasn't aware of until now)
let big = new Array(1e5).fill(99);
let i, x;
console.time("concat-big");
for (i = 0; i < 1e2; i++) x = [].concat(big);
console.timeEnd("concat-big");
console.time("spread-big");
for (i = 0; i < 1e2; i++) x = [...big];
console.timeEnd("spread-big");
console.time("flat-big");
for (i = 0; i < 1e2; i++) x = [[], big].flat();
console.timeEnd("flat-big");
let a = new Array(1e3).fill(99);
let b = new Array(1e3).fill(99);
let c = new Array(1e3).fill(99);
let d = new Array(1e3).fill(99);
console.time("concat-many");
for (i = 0; i < 1e2; i++) x = [1, 2, 3].concat(a, b, c, d);
console.timeEnd("concat-many");
console.time("spread-many");
for (i = 0; i < 1e2; i++) x = [1, 2, 3, ...a, ...b, ...c, ...d];
console.timeEnd("spread-many");
console.time("flat-many");
for (i = 0; i < 1e2; i++) x = [1, 2, 3, a, b, c, d].flat();
console.timeEnd("flat-many");

count items in an one array, if the number of instances is the same as the length of a different array, return the item, javascript

I have two arrays, id like to return the items from array1 if they appear as much as the length of array2
I understand how to do this in python, but I cant figure out how to do it in javascript
arr1 = ['a', 'b', 'c', 'c']
arr2 = ['one', 'two']
arr3 = []
for i in arr1:
if arr1.count(i) == len(arr2):
arr3.append(i)
desired result would be ['c']
Can someone please help me write this in javascript?
You can use reduce and filter
Here idea is :-
First use elements of arr1 as key on obj, if key is already there increment it's value, else set it to zero,
Now take the key's of obj and filter if it's value is equal to length of arr2
let arr1 = ['a', 'b', 'c', 'c']
let arr2 = ['one', 'two']
let obj = arr1.reduce((op,inp)=>{
let key = inp.toLowerCase()
op[key] = op[key] || 0
op[key]++
return op
},{})
let final = Object.keys(obj).filter(key=>{
return obj[key] === arr2.length
})
console.log(final)
Note :- Here i ignored case, if you want both case to be different than you can remove this line
let key = inp.toLowerCase()
Nested loops could do that. One for the for, and one for the count:
var arr1 = ['a', 'b', 'c', 'c'];
var arr2 = ['one', 'two'];
var arr3 = [];
var unique = new Set(arr1);
var len2 = arr2.length;
for(var i of unique){
var count = 0;
for(var j of arr1)
if(j === i)
count++;
if(count == len2)
arr3.push(i);
}
console.log(arr3);
Side remark: I think that Python code would result in ['c','c'], though I have not actually tried.
First use reduce() to create object of whose keys will elements of arr1 and value will be their count. Then filter() its keys according to given condition.
let arr1 = ['a', 'b', 'c', 'c']
let arr2 = ['one', 'two']
const obj = arr1.reduce((ac,a) => (ac[a] = ac[a] + 1 || 1, ac),{});
let res = Object.keys(obj).filter(k => obj[k] === arr2.length);
console.log(res)

Transforming arrays / objects: how can I create dynamically named objects within a for loop?

Trying to manipulate this:
input = [
[ ['a','b'], ['c','d'], ['e','f'] ],
[ ['g','h'], ['i','j'], ]
]
to
output = [
{a: 'b', c: 'd', e: 'f'},
{g: 'h', i: 'j'},
]
Here's what I have so far:
function transform(array) {
result = [];
for (var i=0; i<array.length; i++){
for (var j=0; j<array[i].length; j++){
// How can I create an object here?
object.array[i][j][0] = array[i][j][1];
}
}
return result;
}
I'm trying to solve this as a coding challenge so I don't necessarily want the answer but I'm unsure of how to proceed. Since the number of arrays that have a pair of strings inside is not uniform (for instance, first set of arrays within the input array has 3 sets, and the second set has 2 sets, I reckon I need to dynamically create objects within each loop that I can add to the results array at the end. How can I do this?
I don't think I'm supposed to use any sort of fancier / higher order functions. The goal is to build up my familiarity with the fundamentals.
You can use reduce to process the outer and inner arrays, e.g.
var input = [
[['a','b'], ['c','d'],['e','f'] ],
[['g','h'], ['i','j'],]
];
// For each outer array
var result = input.reduce(function(acc, a){
// Create an object from the inner arrays
acc.push(a.reduce(function(acc, a) {
acc[a[0]] = a[1];
return acc;
},{}));
return acc;
}, []);
console.log('With reduce\n');
console.log(result);
// Same algorithm using for loops:
var result2 = [];
// For each outer array
for (var i=0, iLen=input.length; i<iLen; i++) {
var acc = {};
var a = input[i];
// Loop over the inner arrays to build an object,
// then push into result array
for (var j=0, jLen=a.length; j<jLen; j++) {
var b = a[j]
acc[b[0]] = b[1];
}
result2.push(acc);
}
console.log('\nWith loops')
console.log(result2);
Input.reduce((memo,num) => {
memo[num[0]]=num[1];
return memo;
},{})
You can use nested for..of loops to iterate each inner array, create object, set property to element at index 0, value to element at index 1, push object to array at completion of nested for..of loop
let input = [
[
['a', 'b'],
['c', 'd'],
['e', 'f']
],
[
['g', 'h'],
['i', 'j']
]
];
let output = [];
for (let arr of input) {
let o = {};
for (let [key, value] of arr) {
o[key] = value;
}
output.push(o);
}
console.log(output);

Javascript Concat Values of Two Arrays

I have two arrays like this:
const a = ['a', 'b', 'c', 'd'];
const b = ['1', '2', '3', '4'];
I'm trying to make a new array like this:
const c = ['a1', 'b2', 'c3', 'd4'];
I tried it this way:
const c = [];
c.push([`${a[0]}${b[0]}`, `${a[1]}${b[1]}`, `${a[2]}${b[2]}`, `${a[3]}${b[3]}`]);
With actually looping through data and doing this took 17400ms.
I took out the c.push([........]); and it dropped to 1250ms.
Why does this take so long to do?
And what is the best way to do this?
you can use .map to achieve that. map a, then use index on each loop to get element of b.
const a = ['a', 'b', 'c', 'd'];
const b = ['1', '2', '3', '4'];
var c = a.map(function (d, i) {
return d + String(b[i])
})
console.log(c)
// ["a1", "b2", "c3", "d4"]
cleaner code using es6:
var c = a.map((d, i) => `${d}${b[i]}`)
A simple loop.
const a = ['a', 'b', 'c', 'd', 'e'];
const b = ['1', '2', '3'];
var result = [];
for (var i = 0; i < a.length; i++) {
result[i] = a[i] + b[i];
}
alert(result);
As I suspected and you confirmed, the real problem is that you do too many push.
push modifies the length of the array. The specification does not enforce any data structure for arrays, but for non-sparse ones, implementations usually use lists which store the values consecutively in memory. That's problematic when you change the length of the array, because the additional data could not fit in the place in memory where the data currently is, so all data must be moved. push ends up being constant in amortized time instead of just constant.
However, if you know the length of the resulting array beforehand, it's usually better to preallocate.
Then, instead of
var array = [];
for(var i=0; i<2e4; ++i)
array.push([a[0]+b[0], a[1]+b[1], a[2]+b[2], a[3]+b[3]]);
I would use
var array = new Array(2e4);
for(var i=0; i<2e4; ++i)
array[i] = [a[0]+b[0], a[1]+b[1], a[2]+b[2], a[3]+b[3]];

Merge two multidimensional arrays, but cant duplicate using datetime

I wish to merge two arrays into one, but cant duplicate using datetime, basically cant merge two results from both arrays with the same datetime.
In each array, the datetime is never repeated.
Both arrays have the same structure, with the same exact positions.
Each array have +60 sub arrays.
example:
Array1 = [[a,b,0000-00-00 00:00],[c,d,0000-00-00 00:59],[e,f,0000-00-00 00:10]];
Array2 = [[z,x,0000-00-00 00:00],[h,s,0000-00-00 00:49],[e,f,0000-00-00 00:20]];
Array12 = [[a,b,0000-00-00 00:00],[c,d,0000-00-00 00:59],[e,f,0000-00-00 00:10],[h,s,0000-00-00 00:49],[e,f,0000-00-00 00:20]];
How can i make this work? I tried a lot of functions, but cant get this working.
Thanks.
If I'm correct you are trying to merge the arrays based in timestamps. Try out this fiddle
var Array1 = [
['a', 'b', '0000-00-00 00:00'],
['c', 'd', '0000-00-00 00:59'],
['e', 'f', '0000-00-00 00:10']
];
var Array2 = [
['z', 'x', '0000-00-00 00:00'],
['h', 's', '0000-00-00 00:49'],
['e', 'f', '0000-00-00 00:20']
];
function mergeArrays(arr1, arr2) {
var merger = {};
for (var i = 0; i < arr1.length; i++) {
merger[arr1[i][2]] = [arr1[i][0], arr1[i][1], arr1[i][2]];
}
for (var i = 0; i < arr2.length; i++) {
if (!(arr2[i][2] in merger)) {
merger[arr2[i][2]] = [arr2[i][0], arr2[i][1], arr2[i][2]];
}
}
var output = [];
for (var key in merger) {
output.push(merger[key]);
}
return output;
}
var result = mergeArrays(Array1, Array2);
console.log(result);

Categories

Resources