Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last month.
Improve this question
I have two arrays and need to change the order of elements in a way to be similar. by property c. i should get index of object of property c: 2 for example and place it to the right index in arrB . JavaScript
const arrA= [{
a: 1,
b: [{
c: 2,
d: 3
},
{
c: 5,
f: 6
}
]
},
{
a: 2,
b: [{
c: 2,
d: 3
},
{
c: 5,
f: 6
}
]
}
];
const arrB = [{
a: 1,
b: [{
c: 5,
f: 6
},
{
c: 2,
d: 3
},
]
},
{
a: 2,
b: [{
c: 5,
f: 6
},
{
c: 2,
d: 3
},
]
}
];
What I need is to sort arrB in order of all elements in key b to have the same order like in arrA. in reality this object contain more fields and they aren't similar. but they have the same property c by which i should change the order
const arrB= [{
a: 1,
b: [{
c: 2,
d: 3
},
{
c: 5,
f: 6
}
]
},
{
a: 2,
b: [{
c: 2,
d: 3
},
{
c: 5,
f: 6
}
]
}
];
You can use sort() to order the elements based on the indexes of the matching elements in arrA[i].b.
const arrA = [{
a: 1,
b: [{
c: 2,
d: 3
},
{
c: 5,
f: 6
}
]
},
{
a: 2,
b: [{
c: 2,
d: 3
},
{
c: 5,
f: 6
}
]
}
];
const arrB = [{
a: 1,
b: [{
c: 5,
f: 6
},
{
c: 2,
d: 3
},
]
},
{
a: 2,
b: [{
c: 5,
f: 6
},
{
c: 2,
d: 3
},
]
}
];
arrB.forEach((item, i) =>
item.b.sort((el1, el2) => arrA[i].b.findIndex(el => el.c == el1.c) - arrA[i].b.findIndex(el => el.c == el2.c)));
console.log(arrB);
I have two JSON data with multi-stage nesting. I want compare it by following conditions:
1) if name in the first object equal to name in the second object compare them prop arrays, else if nothing equal names in two object return empty array;
2) compare objects into two prop arrays and find difference;
3) return new object with difference from first and second arrays.
const p1 = [{
name: 'B [1]', // name equals and prop differnce, then comparing it
prop: [{
A: { A: 1, B: 2 },
B: { A: 1, B: 2 },
C: { C: 78, D: 4, T: 7, } }],
}, {
name: 'B [2]', // name equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
B: { A: 1, B: 2 },
D: { C: 3, D: 4, Y: 13 } }],
}, {
name: 'B [3]', // name equals and prop differnce, then comparing it
prop: [{
E: { A: 1, B: 2 },
R: { A: 1, B: 2 },
T: { C: 3, D: 4, } }],
}, {
name: 'B [4]', // name and prop equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
S: { A: 1, B: 2 },
D: { C: 3, D: 4, } }],
}]
const p2 = [{
name: 'B [1]', // name equals and prop differnce, then comparing it
prop: [{
A: { A: 1, B: 8 },
B: { A: 1, B: 2 },
C: { C: 3, T: 7, O: 9 } }],
}, {
name: 'B [6]', // name not equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
B: { A: 1, B: 2 },
D: { C: 3, D: 4 } }],
}, {
name: 'B [3]', // name equals and prop differnce, then comparing it
prop: [{
E: { A: 1, B: 2 },
R: { A: 1, B: 2, U: 150 },
T: { C: 3, D: 4, } }],
}, {
name: 'B [4]', // name and prop equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
S: { A: 1, B: 2 },
D: { C: 3, D: 4, } }],
}]
The result should look like this:
const result = [{
name: 'B [1]',
propOne: [{
A: { B: 2 },
C: { C: 78, D: 4, O: 'Missing' }
}],
propTwo: [{
A: { B: 8 },
C: { C: 3, D: 'Missing', O: 9 }
}],
},{
name: 'B [3]',
propOne: [{
R: { U: 'Missing' }
}],
propTwo: [{
R: { U: 150 }
}]
}]
I also bitterly attach my worthless code here, which does nothing.
const compare = (p1, p2) => {
return p1.reduce((acc, curr) => {
p2.reduce((acc2, curr2) => {
if (curr.name === curr2.name) {
const keys1 = R.fromPairs(Object.keys(curr.prop[0]).map(x => ([x, curr.prop[0][x]])));
const keys2 = R.fromPairs(Object.keys(curr2.prop[0]).map(x => ([x, curr2.prop[0][x]])));
}
return acc;
}, [])
return acc;
}, [])
}
I would be extremely grateful for any help and advice.
All the difficulty resides in specing the expected behaviour of the comparison function:
for two objects (that I refer as values) a and b: {A:1,B:2} and {A:1,B:3,C:4}
the output of cmp(a,b) shall be:
foreach key of a:
if a[key] != b[key] (or b does not have k prop)
diff[key] = a[key]
else (value is equal, no diff)
foreach key of b not in a
diff[key] = Missing
hence (e.g) {B:2, C:'Missing'}
when comparing the values, if diff is empty, you can skip the current prop and when comparing props if the diff is empty skip the record (as if names were different)
function cmp(x,y){
let a = x.prop[0];
let b = y.prop[0];
return Object.keys(a).reduce((o,k)=>{
//compare the right value (such as { A: 1, B: 2 }). assumes primitive types
let u = a[k];
let v = b[k];
let diff = Object.keys(u).reduce((o,k)=>{
return u[k]==v[k]?o:(o[k] = u[k],o)
},{})
Object.keys(v).reduce((o,k)=>{
return u.hasOwnProperty(k)?o:(o[k]='Missing',o);
}, diff);
if(Object.keys(diff).length){
o[k] = diff;
}
return o;
},{});
}
function diff(p1,p2){
return p1.flatMap((o,i)=>{
if(p2[i].name != p1[i].name){
return []
}
let a = p1[i];
let b = p2[i];
let res = cmp(a,b);
if(!Object.keys(res).length){
return [];
}
return {name: a.name, propOne:res, propTwo:cmp(b,a)}
})
};
const p1 = [{
name: 'B [1]', // name equals and prop differnce, then comparing it
prop: [{
A: { A: 1, B: 2 },
B: { A: 1, B: 2 },
C: { C: 78, D: 4, T: 7, } }],
}, {
name: 'B [2]', // name equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
B: { A: 1, B: 2 },
D: { C: 3, D: 4, Y: 13 } }],
}, {
name: 'B [3]', // name equals and prop differnce, then comparing it
prop: [{
E: { A: 1, B: 2 },
R: { A: 1, B: 2 },
T: { C: 3, D: 4, } }],
}, {
name: 'B [4]', // name and prop equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
S: { A: 1, B: 2 },
D: { C: 3, D: 4, } }],
}]
const p2 = [{
name: 'B [1]', // name equals and prop differnce, then comparing it
prop: [{
A: { A: 1, B: 8 },
B: { A: 1, B: 2 },
C: { C: 3, T: 7, O: 9 } }],
}, {
name: 'B [6]', // name not equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
B: { A: 1, B: 2 },
D: { C: 3, D: 4 } }],
}, {
name: 'B [3]', // name equals and prop differnce, then comparing it
prop: [{
E: { A: 1, B: 2 },
R: { A: 1, B: 2, U: 150 },
T: { C: 3, D: 4, } }],
}, {
name: 'B [4]', // name and prop equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
S: { A: 1, B: 2 },
D: { C: 3, D: 4, } }],
}];
console.log('result', JSON.stringify(diff(p1,p2),null,2))
It's kinda late, and i don't have time anymore for extensive tests right now, so this may have some bugs for unexpected cases (i hope not though). It's way too long for a comment, and discarding it would be a bit of a waste.
The below code contains two ways: one constructive, starting with empty objects, building up, and one the other way around, starting with the full objects, and then deleting.
Note, that in your data structures, there are several "one element arrays". If these can contain more than one element (they don't really make much sense to me, it's already objects within arrays within arrays, giving plenty of room for additional props), there needs to be one or two extra map steps, no big issue though.
const p1 = [{
name: 'B [1]', // name equals and prop differnce, then comparing it
prop: [{
A: { A: 1, B: 2 },
B: { A: 1, B: 2 },
C: { C: 78, D: 4, T: 7, }
}],
}, {
name: 'B [2]', // name equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
B: { A: 1, B: 2 },
D: { C: 3, D: 4, Y: 13 }
}],
}, {
name: 'B [3]', // name equals and prop differnce, then comparing it
prop: [{
E: { A: 1, B: 2 },
R: { A: 1, B: 2 },
T: { C: 3, D: 4, }
}],
}, {
name: 'B [4]', // name and prop equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
S: { A: 1, B: 2 },
D: { C: 3, D: 4, }
}],
}];
const p2 = [{
name: 'B [1]', // name equals and prop differnce, then comparing it
prop: [{
A: { A: 1, B: 8 },
B: { A: 1, B: 2 },
C: { C: 3, T: 7, O: 9 }
}],
}, {
name: 'B [6]', // name not equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
B: { A: 1, B: 2 },
D: { C: 3, D: 4 }
}],
}, {
name: 'B [3]', // name equals and prop differnce, then comparing it
prop: [{
E: { A: 1, B: 2 },
R: { A: 1, B: 2, U: 150 },
T: { C: 3, D: 4, }
}],
}, {
name: 'B [4]', // name and prop equals, then skiping it
prop: [{
A: { A: 1, B: 2 },
S: { A: 1, B: 2 },
D: { C: 3, D: 4, }
}],
}];
const result = [{
name: 'B [1]',
propOne: [{
A: { B: 2 },
C: { C: 78, D: 4, O: 'Missing' }
}],
propTwo: [{
A: { B: 8 },
C: { C: 3, D: 'Missing', O: 9 }
}],
},{
name: 'B [3]',
propOne: [{
R: { U: 'Missing' }
}],
propTwo: [{
R: { U: 150 }
}]
}]
const diffDestructive = (a, b) => {
/**
* Copy the objects, remove all identical properties recursively,
* then add "Missing" properties for all properties from either side
* that doesn't exist on the other.
*/
const remove = (x, y) => {
for (let key of Object.keys(x)) {
// hasOwnProperty is only for the degenerate case { prop: undefined }
if (x[key] === y[key] && y.hasOwnProperty(key)) {
delete x[key];
delete y[key];
} // typeof null === "object", therefore an additional check is needed
else if (x[key] && typeof x[key] === "object" && y[key] && typeof y[key] === "object") {
remove(x[key], y[key]);
if ([x, y].every(e => Object.keys(e[key]).length === 0)) {
delete x[key];
delete y[key];
}
}
}
};
const addMissingNotes = (x, y) => {
for (let key of Object.keys(x)) {
if (!(y.hasOwnProperty(key))) y[key] = "Missing";
else if (x[key] && typeof x[key] === "object" && y[key] && typeof y[key] === "object")
addMissingNotes(x[key], y[key]);
}
};
// quick and dirty object deep-copy
let [modA, modB] = [a, b].map(e => JSON.parse(JSON.stringify(e)));
remove(modA, modB);
addMissingNotes(modA, modB);
addMissingNotes(modB, modA);
return [modA, modB];
};
const diffConstructive = (a, b) => {
/**
* Add differing properties to the result step by step.
* Nested objects are handled recursively.
*/
let diffA = {}, diffB = {};
for (let key of Object.keys(a)) {
//properties that a and b share
if (b.hasOwnProperty(key)) {
if (a[key] && typeof a[key] === "object" && b[key] && typeof b[key] === "object") {
let subDiffs = diffConstructive(a[key], b[key]);
// The way the construction works, Object.keys(subDiffs[0]).length !== 0 would be enough.
if (subDiffs.some(e => Object.keys(e).length !== 0)) {
[diffA[key], diffB[key]] = subDiffs;
}
} else if (a[key] !== b[key]) {
diffA[key] = a[key];
diffB[key] = b[key];
}
} // properties that a has but b doesn't
else {
diffA[key] = a[key];
diffB[key] = "Missing";
}
}
// properties that b has but a doesn't
for (let key of Object.keys(b)) {
if (!a.hasOwnProperty(key)) {
diffB[key] = b[key];
diffA[key] = "Missing";
}
}
return [diffA, diffB];
};
const compare = (a, b, method) => a
.map((e, i) => [e, b[i]])
//same name only
.filter(([a, b]) => a.name === b.name)
// formatting
.map(([a, b]) => {
const [diffA, diffB] = method(a.prop[0], b.prop[0]);
return {
name: a.name,
propOne: [diffA],
propTwo: [diffB]
};
})
// There must be a difference
.filter(e => [e.propOne[0], e.propTwo[0]].some(e => Object.keys(e).length !== 0));
const destructive = compare(p1, p2, diffDestructive);
const constructive = compare(p1, p2, diffConstructive);
console.log(`Constructive method gives the wanted result: ${_.isEqual(result, destructive)}`);
console.log(`Destructive method gives the wanted result: ${_.isEqual(result, constructive)}`);
<!--
this is only for a deepequals function, _.isEqual,
and only used for checking the results. I could have copied
one into the code, but why make this even longer...
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.core.min.js"></script>
Are there any libraries or efficient techniques to perform array joins in node JS such that,
A = [ { a: 1, b: 'a' }, { a: 2, b:'b' }, { a: 3, b: 'a' }, { a: 4, b: 'b' } ]
B = [ { a: 1, c: true }, { a: 2, c: true }, { a: 3, c: false } ]
could be joined such that the following results could be produced:
# Intersection on a
C = [ { a: 1, b: 'a', c: true }, { a: 2, b: 'b', c: true }, { a: 3, b: 'a', c: false } ]
# Union on a
D = [ { a: 1, b: 'a', c: true }, { a: 2, b: 'b', c: true }, { a: 3, b: 'a', c: false }, { a: 4, b: 'b' } ]
Is array.map the best solution to this problem?
efficiency is paramount here, since it could be handling huge arrays in production
You're not very specific about how you identify and merge your object.
Using Underscore, the result can be obtained as follow:
_u=require("underscore")
A = [ { a: 1, b: 'a' }, { a: 2, b:'b' }, { a: 3, b: 'a' }, { a: 4, b: 'b' } ]
B = [ { a: 1, c: true }, { a: 2, c: true }, { a: 3, c: false } ]
D = _u.zip(A,B).map(
function(x){
return _u.extend(x[0],x[1]);
}
);
C = _u.zip(A,B).filter(
function(x){
return !!x[1];
}
).map(
function(x){
return _u.extend(x[0],x[1]);
}
);
Is that what you're looking for ?