Merge array into array of objects - javascript

I'm working with API data and I'm trying to build an object combining multiple arrays of data.
Current Arrays:
let name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
let arr1 = ['bar', 'foo', 'foobar']
let arrX = ...
Desired Outcome:
let desiredOutcome = [
{
name: "John",
arr1: "bar", ...
},
{
name: "Jane",
arr1: "foo", ...
},
{
name: "Doe",
arr1: "foobar", ...
}]
I've been trying to play around with Object.assign() but I haven't had any luck:
var merge = Object.assign(obj, arr1 )
Is there a method or methods I could use?

Use .map() to add each element.
let name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
let arr1 = ['bar', 'foo', 'foobar']
let result = name.map((a,i)=>{a.arr1 = arr1[i]; return a})
console.log(result)

You can do it using Array.map
Try the following:
let name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
let arr1 = ['bar', 'foo', 'foobar'];
var result = name.map((o,i) =>Object.assign({"arr1" : arr1[i]},o));
console.log(result);

For an arbitrary count of arrays, you could take an array with the array of objects and the arrays of values and take short hand properties which preserves the name of the array and the values for adding to the result set with Object.assign.
var names = [{ name: "John" }, { name: "Jane" }, { name: "Doe" }],
arr1 = ['bar', 'foo', 'foobar'],
arrX = [1, 2, 3],
result = [names, { arr1 }, { arrX }]
.reduce((r, o) =>
(([k, a]) => a.map((v, i) => Object.assign({}, r[i], { [k]: v })))(Object.entries(o)[0])
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Maybe late but I'll provide some additional explanation on how to use .map:
The .map method creates a new array with the results of calling a provided function on every element in the calling array.
The method takes in one callback as argument. The callback itself can take predefined arguments. Here we will use the first two:
currentValue: e
index: i
Basically, the map method works by associating (literally mapping) each element of the looped array (here name) to the given returned value (here {name: e.name, arr1: arr1[i]}). Mapping is just a bijection between two arrays.
Another word on (e,i) => ({name: e.name, arr1: arr1[i]}):
It is the shorthand syntax called arrow function. It is similar to defining the callback function like so:
function(e,i) {
return { name: e.name, arr1: arr1[i] };
}
Full snippet will look like:
const name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
const arr1 = ['bar', 'foo', 'foobar']
const result = name.map((e,i) => ({ name: e.name, arr1: arr1[i] }))
console.log(result)

Related

How to filter a Array of objects but change order

Lets say I have an array of objects[] and an array of numbers.
let objects = [
{name: apple, id:1},
{name: banana, id:2},
{name: orange, id:5}
];
let numbers = [5, 1];
I want to filter the object array, so only the id that matches with the numbers stays, and the order should be changed to match the numbers array.
result should be [{name: orange, id:5} , {name: apple, id:1} ]
Can I do it with javascript object prototypes? or can I use 3rd party tools like lodash ?
I've tried with code bellow, but it keeps the order of the original list:
result = object.filter(p => {
return numbers.includes(parseInt(p.id));
});
You could map the wanted items by finding the objects.
var objects = [{ name: 'apple', id: 1 }, { name: 'banana', id: 2 }, { name: 'orange', id: 5 }],
numbers = [5, 1],
result = numbers.map(id => objects.find(o => o.id === id));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A canonical solution for finding and reordering could be to use a hash table or Map with a fas access of O(1) and map the wanted parts.
For examle here a solution with an abstraction of the key byhanding over the original data array, an array of wanted values and the key.
const
getObjects = (data, values, key) => values.map(
Map.prototype.get,
objects.reduce((m, o) => m.set(o[key], o), new Map)
),
objects = [{ name: 'apple', id: 1 }, { name: 'banana', id: 2 }, { name: 'orange', id: 5 }],
values = ['banana', 'apple', 'orange'],
result = getObjects(objects, values, 'name');
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It keeps the order because you iterate over objects. If you want to use the order from numbers you have to iterate over numbers.
You didn't say if the ids in numbers had to exist in objects, my solution works even if a stray id finds its way into numbers
let result = []
numbers.forEach(
element => { if (objects.find(anObject => anObject.id == element))
result.push(objects.find(anObject => anObject.id == element)) })
Just Array.map and Array.find. You don't need to Array.filter.
const items = [{name: 'apple', id:1}, {name: 'banana', id:2}, {name: 'orange', id:5}]
const numbers = [5, 1]
const result = numbers.map(n => items.find(i => i.id === n))
console.log(result)
Since you're mapping over numbers, the order of the result Array is identical to numbers.
checkObject(obj, arr) {
let x;
for (x in arr) {
if(arr[x].id == obj.id){
return true;
}
}
return false;
}
let items = [{name: 'apple', id:1}, {name: 'banana', id:2}, {name: 'orange', id:5}]
let numbers = [5, 1];
checkObject(items, numbers);
you can try this function to check if your numbers array matches in your array of Object.

compare values of an array, with the key of an array of objects

As the question indicates, I need to compare the values of an array of this type [1,2,3], with the values of a key of an array of objects [{id: 1, name: 'jhon'}, {id: 2 , name: 'max'}] in this case I want to compare it with the value of the id key, I write the following example of what I need:
if I have this array of objects:
[
   {
      id: 1
      name: 'jhon'
   },
   {
     id: 2,
     name: 'max'
   },
   {
     id: 3,
     name: 'fer'
   }
]
and I have this array:
[1,2,3,4,5]
I need to compare them and obtain the values that are not present in the array of objects, in the previous example the values 4 and 5 are not present in the array of objects, so I need to obtain a new array or the same, but with the values that do not they were, [4,5]
NOTE: this should also work if I have an array, only with numbers that are not present, for example [8,9], they should return those same values.
EDIT: it is possible to obtain in a new array those values that are only present in the array of objects. For example, with the arrays of the previous code:
[{id: 1, name: 'jhon'}, {id: 2, name: 'max'}, {id: 3, name: 'fer'}]
now this array would have the following values [1,4,5] with the previous answers I get the following array [4,5] what is correct. how can I get the values [2,3] in a new array.
You can use filter and find
let arr1 = [{id: 1,name: 'jhon'},{id: 2,name: 'max'},{id: 3,name: 'fer'}]
let arr2 = [1, 2, 3, 4, 5];
let result = arr2.filter(o => !arr1.find(x => x.id === o));
console.log(result);
Update: This is basically the reverse of the first example. You filter the arr2 and check if the id exists on arr1. Then, use map to return the id
let arr1 = [{id: 1,name: 'jhon'},{id: 2,name: 'max'},{id: 3,name: 'fer'}]
let arr2 = [1,4,5];
let result = arr1.filter(o => !arr2.find(x => x === o.id)).map(o=>o.id);
console.log(result);
Doc: filter(), find()
Create a Set from the ids of arr1 using Array.map(). Then filter arr2 by checking if the Set has the number:
const arr1 = [{"id":1,"name":"jhon"},{"id":2,"name":"max"},{"id":3,"name":"fer"}];
const arr2 = [1,2,3,4,5];
const arr3 = [1, 5];
const difference = (arr1, arr2, key) => {
const arr1Values = new Set(key ? arr1.map(({ [key]: v }) => v) : arr1);
return arr2.filter((n) => !arr1Values.has(n));
};
// array of primitives and array of objects
console.log(difference(arr1, arr2, 'id'));
// two arrays of primitives
console.log(difference(arr3, arr2));
You can use filter to check if id property of element in arr2 matches to the current element in arr2
const arr1 = [{"id":1,"name":"jhon"},{"id":2,"name":"max"},{"id":3,"name":"fer"}];
const arr2 = [8,9];
var diffArray = arr2.filter(x => !arr1.filter(y => y.id === x).length);
console.log(diffArray);
Answer
[ 8,9 ]
You can use .filter() and .includes():
let arr1 = [{id: 1,name: 'jhon'},{id: 2, name: 'max'},{id: 3,name: 'fer'}],
arr2 = [1, 2, 3, 4, 5];
let result = (
ids => arr2.filter(n => !ids.includes(n))
)(arr1.map(({id}) => id));
console.log(result);
Description:
Create a new array by extracting only ids from first array using .map().
Filter elements using .filter() and .includes() to filter only those elements of second array that doesn't exists in newly created array.
Docs:
Array.prototype.map()
Array.prototype.filter()
Array.prototype.includes()
Object Destructuring
Arrow Functions
You could take a set with the given ids and then delete the ones of the object. Later return the array with the leftover ids.
var data = [{ id: 1, name: 'jhon' }, { id: 2, name: 'max' }, { id: 3, name: 'fer' }],
ids = [1, 2, 3, 4, 5],
result = Array.from(data.reduce((s, { id }) => (s.delete(id), s), new Set(ids)));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

push item to same nested child in array deep

I can't be able to figure out how to push items to same nested array like :
var arr = ['foo', 'bar', 'buz', 'hello'];
// It should be now look like this:
const output = {name: 'foo', children: [
{name: 'bar', children: [
{name: 'buz', children: [
{name: 'hello', children: []}
]}
]}
]};
Using reduce:
const arr = ['foo', 'bar', 'buz', 'hello'];
const result = arr.reverse().reduce((acc, val) => ({name: val, children: [acc]}), {});
console.log(result);
You can use reduceRight to create the output.
var arr = ['foo', 'bar', 'buz', 'hello'],
result = arr.reduceRight((r, name) => ({name, children: (!Object.keys(r).length ? [] : [r])}), {});
console.log(result);
Using a recursive function:
const arr = ['foo', 'bar', 'buz', 'hello'];
const f = (arr) => ({name: arr.shift(), children: arr.length ? [f(arr)] : []});
const output = f(arr);
console.log(output);
Using a recursive function
function nested (arr) {
if (arr.length === 1)
return { name: arr.shift(), children: [] };
else if (arr.length > 1)
return { name: arr.shift(), children: [nested(arr)] };
}
var array = ['foo', 'bar', 'buz', 'hello']
console.log(nested(array))
I now have an idea on how Array.reduce() method work, so posted another answer without recursive approach. I think it would be better for large tree structure.
The basic idea is that simply reverse the input array and then wrap the innermost object into another and so on.
let arr = ['foo', 'bar', 'buz', 'hello'];
function nest(arr) {
arr = arr.reverse();
let out = [];
arr.forEach(it => {
if(out.length === 0) out = {name: it, children: []}
else {
out = {name: it, children: [out]}
}
});
return out;
}

relative complement of A in B with functional programming

I have to retrieve the values that exist only on Array B, but do not exist on Array A.
From my research, It is called:
relative complement of A in B
Values in the arrays may not be primitives.I need an efficient and functional apporach to this problem.
I have found lodash _.without function, but it supports only array of primitive numbers.
Array A:
[{
id: 1
},
{
id:2
}]
Array B:
[{
id:2
},
{
id:3
}]
result should be:
[{
id:3
}]
this object is the only one who exist on Array B, but not on Array A.
You could use a comparison function which takes two objects and check the id for unequalness.
var aa = [{ id: 1 }, { id: 2 }],
bb = [{ id: 2 }, { id: 3 }],
comparison = (a, b) => a.id !== b.id,
result = bb.filter(b => aa.every(a => comparison(a, b)));
console.log(result);
With a check for equalness
var aa = [{ id: 1 }, { id: 2 }],
bb = [{ id: 2 }, { id: 3 }],
comparison = (a, b) => a.id === b.id,
result = bb.filter(b => aa.every(a => !comparison(a, b)));
console.log(result);
You can use array#filter with array#some. Iterate through arrB and check if the arrA contains that id using array#some and negate the result of array#some.
var arrA = [{id: 1},{id:2}],
arrB = [{id:2},{id:3}],
result = arrB.filter(({id}) => !arrA.some(o => o.id === id));
console.log(result);
You can use array.prototype.filter and array.prototype.findIndex:
var arrayA = [{ id: 1 }, { id: 2 }];
var arrayB = [{ id: 2 }, { id: 3 }];
var result = arrayB.filter(b => arrayA.findIndex(a => a.id === b.id) === -1);
console.log(result);
If you want to use lodash, _.differenceBy could be of use:
relativeComplementOfAinB = _.differenceBy(arrayB, arrayA, v => v.id);

Combine object and arrays

I'm trying to write a function that takes an array of objects, and an unlimited number of arrays, and combines them to form a single object. The inputs would follow this pattern:
let x = [{ name: 'Tom' }, { name: 'John' }, { name: 'Harry' }];
let y = [[1, 2, 3], 'id'];
let z = [['a', 'b', 'c'], 'value'];
combine(x, y, z);
With the second element of y and z acting as the object key. Using these arguments, the function should return the following array:
[
{
name: 'Tom',
id: 1,
value: 'a'
},
{
name: 'John',
id: 2,
value: 'b'
},
{
name: 'Harry',
id: 3,
value: 'c'
},
]
The index of the current object should be used to get the correct element in the array. I have made an attempt at the problem:
function combine(object, ...arrays) {
return object.map((obj, index) => {
let items = arrays.map(arr => ({
[arr[1]]: arr[0][index]
}));
return Object.assign({}, obj, { items });
});
}
This almost does the job, but results in the array items being hidden inside a nested items array, How can I solve this?
You had been assigning an object of object, and the result was a new object with the element items inside (another feature of object literal).
This approach use reduce instead of map and direct assign instead of object literal.
function combine(object, ...arrays) {
return object.map((obj, index) => {
const items = arrays.reduce((acc, arr) => {
acc[arr[1]] = arr[0][index] ;
return acc;
}, {});
return Object.assign({}, obj, items);
});
}
const x = [{ name: 'Tom' }, { name: 'John' }, { name: 'Harry' }];
const y = [[1, 2, 3], 'id'];
const z = [['a', 'b', 'c'], 'value'];
combine(x, y, z);
You can also use the spread operator in the Object.assign, like this:
function combine(object, ...arrays) {
return object.map((obj, index) => {
let items = arrays.map(arr => ({
[arr[1]]: arr[0][index]
}));
return Object.assign({}, obj, ...items);
});
}
This almost does the job, but results in the array items being hidden inside a nested items array
The problem is that items is an array, whereas you only need the current item inside of that particular map callback. No need to nest loops here.
Also I would recommend avoiding multiple properties per combine call. The resulting code would look like this:
function combine(objects, [values, key]) {
return objects.map((o, i) =>
Object.assign({[key]: values[i]}, o)
);
}
combine(combine(x, y), z);
If you then have multiple extensions to do, you can also use
[y, z].reduce(combine, x)
With map and computed keys, you can achieve this.
Here's a working example:
let x = [{
name: 'Tom'
}, {
name: 'John'
}, {
name: 'Harry'
}];
let y = [[1, 2, 3], 'id'];
let z = [['a', 'b', 'c'], 'value'];
let result = [];
x.map(function (el, index) {
result.push(el);
let index = result.length -1;
result[index][y[1]] = y[0][index];
result[index][z[1]] = z[0][index];
});
console.log(result);

Categories

Resources