Reputation: 11982
I have the following situation:
There are an undefined number of arrays. Each array contains some objects. Each of these objects has an id
element, among some other elements.
//Example objects:
f = [{'id': 1, 'name': 'a'}, {'id': 2, 'name': 'b'},
{'id': 3, 'name': 'c'}, {'id': 1, 'name': 'd'}, ...]
g = [{'id': 2, 'name': 'e'}, {'id': 4, 'name': 'f'},
{'id': 3, 'name': 'g'}, {'id': 4, 'name': 'h'}, ...]
h = ...
i = ...
I would like to filter out all the duplicates, not only inside every separate array, but also across the different arrays.
Edit: Two objects are a duplicate when their id
element are the same. Precedence does not matter, first occurrence is fine for this.
I came up with the following function:
function uniqueObjects() {
var seen = [];
for (var i=0; i<arguments.length; i++) {
var arg = arguments[i];
for (var j=0; j<arg.length; j++) {
var elm = arg[j];
var key = elm['id'];
if (seen.indexOf(key) > -1) {
arg.pop(elm);
} else {
seen.push(key);
}
}
}
return arguments;
}
But this does not seem to work properly, since I got the following values:
{'0': [{'id': 1, 'name': 'a'},
{'id': 2, 'name': 'b'},
{'id': 3, 'name': 'c'}],
'1': [{'id': 2, 'name': 'e'},
{'id': 4, 'name': 'f'}]}
Upvotes: 0
Views: 1098
Reputation: 1074285
The main problem is here:
arg.pop(elm);
pop
removes the last element in the array and doesn't take any arguments. You may have meant:
arg.splice(j, 1);
...but then you need to not increment j
if you do that. So that gives us:
function uniqueObjects() {
var seen = [];
for (var i=0; i<arguments.length; i++) {
var arg = arguments[i];
var j = 0; // **
while (j<arg.length) { // ** Made this a while loop
var elm = arg[j];
var key = elm['id'];
if (seen.indexOf(key) > -1) {
arg.splice(j, 1); // ** Remove this entry
} else {
seen.push(key);
++j; // ** Increment j
}
}
}
return arguments;
}
However, there is a much more efficient way to track which id
values you've seen: Using an object:
function uniqueObjects() {
var seen = {};
for (var i=0; i<arguments.length; i++) {
var arg = arguments[i];
var j = 0;
while (j<arg.length) {
var elm = arg[j];
var key = " " + elm['id']; // ** Note the space
if (seen[key]) { // ** Updated check
arg.splice(j, 1);
} else {
seen[key] = true; // ** Set the flag
++j;
}
}
}
return arguments;
}
Why put the space before the id
when creating key
? On the off-chance that you have id
values with names that exist on objects, like toString
or valueOf
. (ES6 will introduce Map
objects and we won't have to do that anymore.)
I'd also recommend not returning the pseudo-array arguments
, but instead using a real array. So:
function uniqueObjects() {
var seen = {};
var args = Array.prototype.slice.call(arguments, 0); // <== Make real array
for (var i=0; i<args.length; i++) {
var arg = args[i];
var j = 0;
while (j<arg.length) {
var elm = arg[j];
var key = " " + elm['id']; // ** Note the space
if (seen[key]) { // ** Updated check
arg.splice(j, 1);
} else {
seen[key] = true; // ** Set the flag
++j;
}
}
}
return args;
}
Live Example:
function uniqueObjects() {
var seen = {};
var args = Array.prototype.slice.call(arguments, 0);
for (var i=0; i<args.length; i++) {
var arg = args[i];
var j = 0;
while (j<arg.length) {
var elm = arg[j];
var key = " " + elm['id']; // ** Note the space
if (seen[key]) { // ** Updated check
arg.splice(j, 1);
} else {
seen[key] = true; // ** Set the flag
++j;
}
}
}
return args;
}
var f = [
{'id': 1, 'name': 'a'}, {'id': 2, 'name': 'b'},
{'id': 3, 'name': 'c'}, {'id': 1, 'name': 'd'}
];
var g = [
{'id': 2, 'name': 'e'}, {'id': 4, 'name': 'f'},
{'id': 3, 'name': 'g'}, {'id': 4, 'name': 'h'}
];
snippet.log("Before: " + JSON.stringify([f, g]));
var result = uniqueObjects(f, g);
snippet.log("After: " + JSON.stringify(result));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Upvotes: 2