Merge objects from two arrays in one - javascript

I have one question about merge objects in array. The biggest problem is how to save this array structure after merger these arrays, because if I use .push() iterate after these array I receive one big array. Look on my data:
[
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
]
]
and
[
[
{ correct: false },
{ correct: true },
{ correct: false },
],
[
{ correct: true },
{ correct: false },
{ correct: false },
]
]
And my point is how merge these two array, to the one array in form like
[
[
{ name: '1', correct: false },
{ name: '2', correct: true },
{ name: '3', correct: false },
],
[
{ name: '4', correct: true },
{ name: '5', correct: false },
{ name: '6', correct: false },
]
]
I tried following loop but I receive one big array, but I need two array
for(i in nameArray) {
for(j in nameArray[i]){
var mergeObj = Object.assign(nameArray,correctArray[j]) //I get undefned
correctArray.push(nameArray[i][j])
}
}

You're close, but you have arrays of arrays of objects, so you need correctArray[i][j]
const nameArray = [
[{ name: '1' },{ name: '2' },{ name: '3' }],
[{ name: '4' },{ name: '5' },{ name: '6' }]
];
const correctArray = [
[{ correct: false },{ correct: true },{ correct: false }],
[{ correct: true },{ correct: false },{ correct: false }]
];
nameArray.forEach((a, i) =>
a.forEach((o, j) => Object.assign(o, correctArray[i][j]))
);
console.log(nameArray);
Here I used .forEach(), which is nicer than using for-in on arrays. The objects in the nameArray are being mutated, so that's your result. You can instead use .map() calls and add an empty object at the start of Object.assign if you don't want to mutate.
const nameArray = [
[{ name: '1' },{ name: '2' },{ name: '3' }],
[{ name: '4' },{ name: '5' },{ name: '6' }]
];
const correctArray = [
[{ correct: false },{ correct: true },{ correct: false }],
[{ correct: true },{ correct: false },{ correct: false }]
];
const result = nameArray.map((a, i) =>
a.map((o, j) => Object.assign({}, o, correctArray[i][j]))
);
console.log(result);

For a new independent array, you could use Array#reduce and build the new object in the matrix.
var array1 = [[{ name: '1' }, { name: '2' }, { name: '3' }], [{ name: '4' }, { name: '5' }, { name: '6' }]],
array2 = [[{ correct: false }, { correct: true }, { correct: false }], [{ correct: true }, { correct: false }, { correct: false }]],
result = [array1, array2].reduce((r, a) =>
(a.forEach((b, i) =>
(r[i] = r[i] || [], b.forEach((c, j) =>
Object.assign(r[i][j] = r[i][j] || {}, c)))), r), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Here's another version that's blown out a little bit, and shows an example of how to handle a degree of nesting in the case of a more complex dataset.
const nameArray = [
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
]
];
const correctArray = [
[
{ correct: false },
{ correct: true },
{ correct: false },
],
[
{ correct: true },
{ correct: false },
{ correct: false },
]
];
var resultArray = []
function each(arr, fn){
let len = arr.length, i = -1
while(++i < len){ fn(arr[i], i) }
}
function addToArray(target, source){
each(source, (elem, index) => {
if(!target[index]){
let newElem = elem.length ? [] : {}
target[index] = newElem
}
if(elem.length){
addToArray(target[index], elem)
} else {
let keys = Object.keys(elem)
each(keys, key => {
if(target[index][key]) console.warn(`Key ${key} already exists, overwriting`)
target[index][key] = elem[key]
})
}
})
}
addToArray(resultArray, nameArray)
addToArray(resultArray, correctArray)
console.log(JSON.stringify(resultArray, null, 2))

I believe that the best approach to your problem it is a recursive function. Something similar to this one:
var a = [
[
{ name: '1' },
{ name: '2' },
{ name: '3' },
],
[
{ name: '4' },
{ name: '5' },
{ name: '6' },
]
]
var b = [
[
{ correct: false },
{ correct: true },
{ correct: false },
],
[
{ correct: true },
{ correct: false },
{ correct: false },
]
]
var c = [
[
{ hello: "world" }
]
];
function mergeRecursive(x,y){
/**
* Ignore functions
*/
if( x instanceof Function ) {
x = undefined;
}
if( y instanceof Function ) {
y = undefined;
}
/**
* Ignore undefineds
*/
if( x == undefined ) {
return y;
}
if( y == undefined ) {
return x;
}
/**
* Get the keys and remove duplicated
*/
var kx = Object.keys(x).filter( (k) => ! ( x[k] instanceof Function ) );
var ky = Object.keys(y).filter( (k) => ! ( y[k] instanceof Function ) );
var ks = kx.concat(
ky.filter(
(e) => kx.indexOf(e) == -1
)
);
/**
* Define the type of the result
*/
var result;
if (x instanceof Array && y instanceof Array ) {
result = [];
ks = ks.map( (k) => 1 * k ); // cast to number
} else {
result = {};
}
/**
* Create the merge object recursively
*/
ks.forEach( (k) => result[k] = mergeRecursive(x[k],y[k]) );
return result;
}
var example1 = mergeRecursive(a,b);
console.log("example 1");
console.log(example1);
var example2 = mergeRecursive(example1,c);
console.log("example 2");
console.log(example2);

let arr1 = [[ { name: '1' }, { name: '2' }, { name: '3' }],[{ name: '4' }, { name: '5' }, { name: '6' }]]
let arr2 = [[{ correct: false },{ correct: true },{ correct: false }],[{ correct: true },{ correct: false },{ correct: false }]]
let arr3 = arr1.map((ele,ind)=>{
return ele.map((e,i)=>Object.assign(e,arr2[ind][i]))
})

Related

loop through an array and check if array inside it has a certain value

I have an array of objects like below
const arr =
[ { a: '', b: '', arr: [ { label: '2' }, { label: '3' } ] }
, { a: '', b: '', arr: [ { label: '1' }, { label: '2' }, { label: '3' } ] }
, { a: '', b: '', arr: [ { label: '1' }, { label: '3' } ] }
]
I need to filter this array and get the objects in which the arr array has the label value of 2.
ie., expected result :
const arr =
[ { a: '', b: '', arr: [ { label: '2' }, { label: '3' } ] }
, { a: '', b: '', arr: [ { label: '1' }, { label: '2' }, { label: '3' } ] }
]
I have tried something like this:
array.forEach((item) => item.arr.filter(i) => i.label === '2')
how would we get back a filtered array looping through this array which has label values as "2" inside the arr array?
How can I achieve the expected result?
With filter, some and destructuring
const arr =
[ { a: '', b: '', arr: [ { label: '2' }, { label: '3' } ] }
, { a: '', b: '', arr: [ { label: '1' }, { label: '2' }, { label: '3' } ] }
, { a: '', b: '', arr: [ { label: '1' }, { label: '3' } ] }
]
const output = arr.filter(({ arr }) => arr.some(({ label }) => label === '2'));
console.log(output)
Like this:
let oldArray = array = [
{
"a":"",
"b":"",
"arr":[
{
"label":"2"
},
{
"label":"3"
}
]
},
{
"a":"",
"b":"",
"arr":[
{
"label":"1"
},
{
"label":"2"
},
{
"label":"3"
}
]
},
{
"a":"",
"b":"",
"arr":[
{
"label":"1"
},
{
"label":"3"
}
]
}
]
let newArray = oldArray.filter(item=>
item.arr && item.arr.filter(inner=> inner.label == "2").length >= 1
)
console.log(newArray)
You can simply achieve this by using Array.filter() method along with Array.some()
Live Demo :
const arr = [
{ a: '', b: '', arr: [ { label: '2' }, { label: '3' }] },
{ a: '', b: '', arr: [ { label: '1' }, { label: '2' }, { label: '3' } ] },
{ a: '', b: '', arr: [ { label: '1' }, { label: '3' }] }
];
const filteredArr = arr.filter(({ arr }) => arr.some(o => o.label === '2'));
console.log(filteredArr);

How to group object inside array

Here is what I have
[
{
typeProgramId: {
name: 'type1',
},
title: 'test1',
},
{
typeProgramId: {
name: 'type1',
},
subTypeProgramId: [{
name: 'sub1',
}],
title: 'test2',
},
{
typeProgramId: {
name: 'type2',
},
title: 'test3',
},
{
typeProgramId: {
name: 'type2',
},
subTypeProgramId: {
name: 'sub2',
},
title: 'test4',
}
]
First I want to group typeProgramId if the title have the same typeProgramId I want to push title into array by each typeProgramId but If the data have typeProgramId and subTypeProgram Id I want to group subtypeProgramId in typeProgramId too. if not empty subtypeProgramId I want to push it in array title inside subtypeProgram Id. I try to use lodash groupBy and many way but it still did not work.
Here is what I want
{
typeProgramId: [{
name: 'type1',
title: [
'test1',
],
subTypeProgramId: {
name: sub1,
title: [
'test2'
]
}
}, {
name: 'type2',
title: [
'test3',
],
subTypeProgramId: [{
name: sub1,
title: [
'test4'
]
}
}]
}
what I do now
let result = _.groupBy(getProgram, function(data) {
return data.typeProgramId
})
result = _.map(result, function(group, data) {
// I think in here I must groupBy subTypeProgramId again
// the data return all string not object after group
return {
typeProgramId: data,
titile: group,
}
})
Please check the below code. I have used reduce function of Array. It produces the expected result.
function updateMem(mem, prgIndex, val){
if(prgIndex < 0) {
mem.typeProgramId.push({});
prgIndex = mem.typeProgramId.length - 1;
}
mem.typeProgramId[prgIndex].name = val.typeProgramId.name;
if(val.subTypeProgramId){
mem.typeProgramId[prgIndex].subTypeProgramId = Object.assign({}, mem.typeProgramId[prgIndex].subTypeProgramId || {}, {"name" : val.subTypeProgramId.name, "title": []});
mem.typeProgramId[prgIndex].subTypeProgramId.title.push(val.title);
} else {
mem.typeProgramId[prgIndex].title = (mem.typeProgramId[prgIndex].title ? mem.typeProgramId[prgIndex].title : []);
mem.typeProgramId[prgIndex].title.push(val.title);
}
};
arr.reduce((mem, val) => {
var prgIndex = mem.typeProgramId.findIndex((p) => p.name === val.typeProgramId.name);
updateMem(mem, prgIndex, val);
return mem;
}, {typeProgramId: []});

Javascript Alphabetised Object Key Sorting

I'm wondering, I have the following data structure:
data = [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
{
name: 'Bravo',
},
{
name: 'Brafo',
},
{
name: 'Charlie',
},
{
name: 'Charly',
},
...
{
name: 'Zulu',
},
{
name: 'Zulo',
},
]
I'm expecting there to be at least one, usually more, key for each letter of the alphabet. However, if there isn't a single data.name I would still like in the below data structure to have an empty domains array [].
I was wondering, how could this be manipulated into the following data structure:
data = {
a: {
domains: [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
],
},
b: {
domains: [
...
]
},
...
z: {
domains: [
...
]
},
};
I have used a few methods, which involved a pre-constructed "alphbetised" key = object array, then filtered each on the first letter of the data.name value...but I was wondering if there was a standard and performant method to acheive this?
Using reduce()
const data = [{name:"Alpha"},{name:"Alfa"},{name:"Bravo"},{name:"Brafo"},{name:"Charlie"},{name:"Charly"},{name:"Zulu"},{name:"Zulo"}]
const res = data.reduce((a, v) => {
// prepare key
let key = v.name.substring(0,1).toLowerCase()
// check key in accumulator
if (!a[key]) {
// assign domain object
a[key] = {domains: []}
}
// push domain array
a[key].domains.push(v)
return a
}, {})
console.log(res)
Here is what you want:
data = [
{
name: 'Alpha',
},
{
name: 'Alfa',
},
{
name: 'Bravo',
},
{
name: 'Brafo',
},
{
name: 'Charlie',
},
{
name: 'Charly',
},
{
name: 'Zulu',
},
{
name: 'Zulo',
},
];
console.log(data.reduce((a, c) => {
const firstLetter = c.name[0].toLowerCase();
if (a[firstLetter]) {
a[firstLetter].domains.push(c);
} else {
a[firstLetter] = { domains: [c] };
}
return a;
}, {}));

Remove 'value' key from nested array of objects

I got an array like this:
rows: [
[
{ title: 'Test', value: 1 },
{ title: 'Test2', value: 2 },
{ title: 'Test3', value: 3 },
],
[
{ title: 'Test4', value: 4 },
{ title: 'Test5', value: 5 },
],
[
{ title: 'Test6', value: 6 },
{ title: 'Test7', value: 7 },
]
]
Now I'd like to unset the key value in each nested object.
At the moment I am doing:
rows.map(function(fields){
return fields.map(function(field){
field.value = '';
return field;
})
});
Do you see a better way to do this?
function deleteKeyFromObject(inputObject) {
for (let[currentObjectKey,currentObjectValue] of Object.entries(inputObject)) {
if (currentObjectKey === 'value') {
delete inputObject.value;
} else if (Array.isArray(currentObjectValue)) {
deleteObjectFromArray(currentObjectValue);
} else if (typeof currentObjectValue === 'object') {
deleteKeyFromObject(currentObjectValue);
}
}
;
}
function deleteObjectFromArray(inputArray) {
for (let currentIndex = 0; currentIndex < inputArray.length; currentIndex++) {
let currentElement = inputArray[currentIndex];
if (Array.isArray(currentElement)) {
deleteObjectFromArray(currentElement);
} else if (typeof currentElement === 'object') {
deleteKeyFromObject(currentElement);
}
}
;
}
var data1 = {
rows: [[{
title: 'Test',
value: 1
}, {
title: 'Test2',
value: 2
}, {
title: 'Test3',
value: 3
}, ], [{
title: 'Test4',
value: 4
}, {
title: 'Test5',
value: 5
}, ], [{
title: 'Test6',
value: 6
}, {
title: 'Test7',
value: 7
}, ]]
}
deleteKeyFromObject(data1);
console.log(data1);
Probably not modifying existing objects could be a better practice:
rows = rows.map(fields => {
return fields.map(field => {
return {...field, value: ''};
})
});
You can use delete to explicitly remove a property and its value. No map needed:
var ob = {foo:'bar',fizz:'buzz'};
console.log(ob); // Object { foo: "bar", fizz: "buzz" }
delete ob.fizz;
console.log(ob); // Object { foo: "bar" }

Merge Array of Objects by Property using Lodash

I have two arrays of objects that represent email addresses that have a label and a value:
var original = [
{
label: 'private',
value: 'private#johndoe.com'
},
{
label: 'work',
value: 'work#johndoe.com'
}
];
var update = [
{
label: 'private',
value: 'me#johndoe.com'
},
{
label: 'school',
value: 'schhol#johndoe.com'
}
];
Now I want to compare and merge the two arrays by the label field, so that the result would look like this:
var result = [
{
label: 'private',
value: 'me#johndoe.com'
},
{
label: 'work',
value: 'work#johndoe.com'
},
{
label: 'school',
value: 'schol#johndoe.com'
}
]
How can I do this e.g. using lodash?
_.unionBy():
This method is like _.union except that it accepts iteratee which is invoked for each element of each arrays to generate the criterion by which uniqueness is computed. Result values are chosen from the first array in which the value occurs.
var original = [
{ label: 'private', value: 'private#johndoe.com' },
{ label: 'work', value: 'work#johndoe.com' }
];
var update = [
{ label: 'private', value: 'me#johndoe.com' },
{ label: 'school', value: 'schol#johndoe.com' }
];
var result = _.unionBy(update, original, "label");
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
Convert the lists to objects keyed by label, merge them by _.assign, and convert it back to an array. It will even retain order of the items on most browsers.
var original = [
{
label: 'private',
value: 'private#johndoe.com'
},
{
label: 'work',
value: 'work#johndoe.com'
}
];
var update = [
{
label: 'private',
value: 'me#johndoe.com'
},
{
label: 'school',
value: 'schol#johndoe.com'
}
];
console.log(
_.map(
_.assign(
_.mapKeys(original, v => v.label),
_.mapKeys(update, v => v.label)
)
)
);
// or remove more duplicated code using spread
console.log(
_.map(
_.assign(
...[original, update].map(
coll => _.mapKeys(coll, v => v.label)
)
)
)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.js"></script>
Perhaps a bit late, but all the solutions I have seen don't join both arrays correctly, they use one of the arrays to loop on and any excess elements in the second array don't get added (assuming this is what is required).
The right way is to sort both arrays and move forward within both arrays, merging the matches elements and adding the missing elements from both arrays.
Please find full solution below. This also takes O(n+m) which is the best you can get (without the computational costs for sort itself). In my code I already got the data sorted from the database.
function mergeObjectsBasedOnKey(array1, array2, compareFn, mergeFn, alreadySorted) {
var array1Index = 0;
var array2Index = 0;
const merged = [];
if (!alreadySorted) {
array1.sort(compareFn);
array2.sort(compareFn);
}
while (array1Index < array1.length && array2Index < array2.length) {
var comparedValue = compareFn(array1[array1Index], array2[array2Index]);
if (comparedValue === 0) {
merged.push(mergeFn(array1[array1Index], array2[array2Index]));
array1Index++;
array2Index++;
} else if (comparedValue < 0) {
merged.push(mergeFn(array1[array1Index]));
array1Index++;
} else {
merged.push(mergeFn(array2[array2Index]));
array2Index++;
}
}
while (array1Index < array1.length) {
merged.push(mergeFn(array1[array1Index]));
array1Index++;
}
while (array2Index < array2.length) {
merged.push(mergeFn(array2[array2Index]));
array2Index++;
}
return merged;
}
const array1 = [{
"id": 10,
isArray1: true
},
{
"id": 11,
isArray1: true
},
{
"id": 12,
isArray1: true
},
];
const array2 = [{
"id": 8,
isArray2: true
},
{
"id": 11,
isArray2: true
},
{
"id": 15,
isArray2: true
},
];
const result = mergeObjectsBasedOnKey(array1, array2, function(a, b) {
return a.id - b.id;
}, function(a, b) {
if (b) {
return _.merge(a, b);
}
return _.merge(a, {
isArray1: true,
isArray2: true
});
});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
And the results would be:
[ { id: 8, isArray2: true, isArray1: true },
{ id: 10, isArray1: true, isArray2: true },
{ id: 11, isArray1: true, isArray2: true },
{ id: 12, isArray1: true, isArray2: true },
{ id: 15, isArray2: true, isArray1: true } ]
In case you are using lodash 3.x where _.unionBy() was not there, you can combine _.union() and _.uniq() to get the same result.
var original = [
{ label: 'private', value: 'private#johndoe.com' },
{ label: 'work', value: 'work#johndoe.com' }
];
var update = [
{ label: 'private', value: 'me#johndoe.com' },
{ label: 'school', value: 'schol#johndoe.com' }
];
var result = _.uniq(_.union(update, original), "label");
console.log(result);
I know it is not what asked for but just in case someone stumbled up on this page here is how you do this in ramda:
var original = [
{ label: 'private', value: 'private#johndoe.com' },
{ label: 'work', value: 'work#johndoe.com' }
];
var updated = [
{ label: 'private', value: 'me#johndoe.com' },
{ label: 'school', value: 'schol#johndoe.com' }
];
unionWith(eqBy(prop('label')), updated, original);
Here is another way to merge two objects using Lodash:
let a = [{
content: 'aaa',
name: 'bbb2'
},
{
content: 'aad',
name: 'ccd'
}
];
let b = [{
content: 'aaa',
name: 'bbb'
},
{
content: 'aad1',
name: 'ccd1'
}
];
let c = [...a, ...b];
let d = _.uniq(c, function(data) {
return data.content;
})
console.log(d);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
Perhaps a bit late, but all the solutions I have seen don't join both arrays correctly, they use one of the arrays to loop on and any excess elements in the second array don't get added (assuming this is what is required).
I had the same observation so put something together myself. This is working for my use case, which is to merge each object if the value of the 'label' field matches:
const dataSetHashes = dataSets.map(dataSet => _.keyBy(dataSet, 'label'))
const resultHash = _.merge(
{},
...dataSetLookups
)
const result = Object.values(resultLookup)

Categories