How to "uniqueify" a javascript array of objects? - javascript

How does one remove objects in an array that match across all keys and values? I've seen variations of this question but only for a particular field.
For example, given the following input the output would look like so:
> var a = [
{a:1, b:'x'},
{a:1, b:'y'},
{a:1, b:'y'},
{a:2, b:'x'},
{a:2, b:'y'},
{a:2, b:'y'},
{a:2, b:'x'},
{b:'y', a:2},
{a:2, b:'y', c:'surprise!'}
]
> blackbox(a)
[
{a:1, b:'x'},
{a:1, b:'y'},
{a:2, b:'x'},
{a:2, b:'y'},
{a:2, b:'y', c:'surprise!'}
]
Ideally blackbox isn't hard-coded with the keys.

BTW, here the hack that I have now. It turns each (sorted) object into a string and checks to see if it's seen that string before.
Regardless, there's got to be a more elegant solution out there!
> function uniq(a) {
var keys = new Set();
return a.filter(function(row) {
var key = Object.entries(row).sort().toString();
var uniq = !keys.has(key);
keys.add(key);
return uniq;
});
}
> a =
[ { a: 1, b: 'x' },
{ a: 1, b: 'y' },
{ a: 1, b: 'y' },
{ a: 2, b: 'x' },
{ a: 2, b: 'y' },
{ a: 2, b: 'y' },
{ a: 2, b: 'x' },
{ b: 'y', a: 2 },
{ a: 2, b: 'y', c: 'surprise!' },
{ c: 'surprise!', a: 2, b: 'y' } ]
> uniq(a)
[ { a: 1, b: 'x' },
{ a: 1, b: 'y' },
{ a: 2, b: 'x' },
{ a: 2, b: 'y' },
{ a: 2, b: 'y', c: 'surprise!' } ]
> b =
[ { a: { b: 1, c: 2 }, b: 1 },
{ b: 1, a: { c: 2, b: 1 } },
{ a: { b: 1, c: 2 }, b: 2 } ]
> uniq(b) // works because nested objects happen to be identical
[ { a: { b: 1, c: 2 }, b: 1 }, { a: { b: 1, c: 2 }, b: 2 } ]
> c =
[ { a: { b: 1, c: 2 }, b: 1 },
{ b: 1, a: { c: 2, b: 1 } },
{ a: { b: 1, c: 2 }, b: 2 },
{ a: { b: 2, c: 1 }, b: 2 } ]
> uniq(c) // fail on nested object
[ { a: { b: 1, c: 2 }, b: 1 }, { a: { b: 1, c: 2 }, b: 2 } ]

Related

How to change index of elements in array of objects according to another array JavaScript? [closed]

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);

how to create a array of values from dictionary having specific key?

Input
dict = [{a: 1, b: 2, c: 3},{a: 4, b: 5, c: 6}, {a: 7, b: 8, c: 9} ]
Output to array having only values of key 'a' in typescript
array = [ 1, 4, 7 ]
Here is how you can use the Array.prototype.map() function:
> dict = [ { a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }, { a: 7, b: 8, c: 9 } ]
[ { a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }, { a: 7, b: 8, c: 9 } ]
> dict.map(({a}) => a)
[ 1, 4, 7 ]
>

Compare two nested array and objects for find difference

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>

How to convert an array of dictionaries to an array of arrays?

How can I easily convert this (array of dictionaries):
[ { a: 1,
b: 1,
c: 'something',
d: 1 },
{ a: 23443,
b: 2111,
c: 'something 2',
d: 1456 }
]
to this (array of arrays):
[ [ 1,
1,
'something',
1 ],
[ 23443,
2111,
'something2',
1456 ]
]
Use Array.prototype.map and for-in loop
The map() method creates a new array with the results of calling a provided function on every element in this array
The for...in statement iterates over the enumerable properties of an object
Try this:
var input = [{
a: 1,
b: 1,
c: 'something',
d: 1
}, {
a: 23443,
b: 2111,
c: 'something2',
d: 1456
}];
var op = input.map(function(inp) {
var arr = [];
for (var i in inp) {
arr.push(inp[i]);
}
return arr;
});
console.log(op);
OR use Object.keys(YOUR_OBJECT):
var input = [{
a: 1,
b: 1,
c: 'something',
d: 1
}, {
a: 23443,
b: 2111,
c: 'something2',
d: 1456
}];
var op = input.map(function(inp) {
return Object.keys(inp).map(function(key) {
return inp[key];
})
});
console.log(op);

Node.js object array data joins

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 ?

Categories