2

Okay, so I am trying to create a function that allows you to input an array of Objects and it will return an array that removed any duplicate objects that reference the same object in memory. There can be objects with the same properties, but they must be different in-memory objects. I know that objects are stored by reference in JS and this is what I have so far:

const unique = array => {
    let set = new Set();
    return array.map((v, index) => { 
        if(set.has(v.id)) {
            return false
        } else {
            set.add(v.id);
            return index;
        } 
    }).filter(e=>e).map(e=>array[e]);
}

Any advice is appreciated, I am trying to make this with a very efficient Big-O. Cheers!

EDIT: So many awesome responses. Right now when I run the script with arbitrary object properties (similar to the answers) and I get an empty array. I am still trying to wrap my head around filtering everything out but on for objects that are referenced in memory. I am not positive how JS handles objects with the same exact key/values. Thanks again!

penntreetz
  • 65
  • 5
  • 1
    sample data and expected results would help greatly here. Also a sample of what the code currently outputs, for comparison, so we can narrow down the problem and propose a fix - after all you haven't actually told us what issue you're facing, so far. Thanks. – ADyson Feb 05 '19 at 16:47
  • Why do you return the index in your callback? Be aware that a zero-index will be filtered out by your filter. – trincot Feb 05 '19 at 16:48
  • 1
    It is a bit confusing what you are asking, since your code suggests that the `id` value determines whether two objects are duplicates of each other, but then your question speaks of *"reference the same object"* – trincot Feb 05 '19 at 17:02
  • I apologize, that is a bit confusing. It could be any key/value pair I now realize, I just wanted to filter by memory reference. Thank you! – penntreetz Feb 05 '19 at 17:10
  • In that case this has been asked before: https://stackoverflow.com/questions/1960473/get-all-unique-values-in-a-javascript-array-remove-duplicates – trincot Feb 05 '19 at 17:11
  • @penntreetz you only want to remove objects with same reference not same props values? – Maheer Ali Feb 05 '19 at 17:11

3 Answers3

2

Simple Set will do the trick

let a = {'a':1}
let b = {'a': 1,'b': 2, }
let c = {'a':1}

let arr = [a,b,c,a,a,b,b,c];

function filterSameMemoryObject(input){
    return new Set([...input])
}

console.log(...filterSameMemoryObject(arr))

I don't think you need so much of code as you're just comparing memory references you can use === --> equality and sameness .

let a = {'a':1}

console.log(a === a )  // return true for same reference
console.log( {} === {}) // return false for not same reference
Code Maniac
  • 37,143
  • 5
  • 39
  • 60
1

The idea of using a Set is nice, but a Map will work even better as then you can do it all in the constructor callback:

const unique = array => [...new Map(array.map(v => [v.id, v])).values()]
// Demo:
var data = [
    { id: 1, name: "obj1" },
    { id: 3, name: "obj3" },
    { id: 1, name: "obj1" }, // dupe
    { id: 2, name: "obj2" },
    { id: 3, name: "obj3" }, // another dupe
]; 
console.log(unique(data));

Addendum

You speak of items that reference the same object in memory. Such a thing does not happen when your array is initialised as a plain literal, but if you assign the same object to several array entries, then you get duplicate references, like so:

const obj = { id: 1, name: "" };
const data = [obj, obj];

This is not the same thing as:

const data = [{ id: 1, name: "" }, { id: 1, name: "" }];

In the second version you have two different references in your array.

I have assumed that you want to "catch" such duplicates as well. If you only consider duplicate what is presented in the first version (shared references), then this was asked before.

trincot
  • 317,000
  • 35
  • 244
  • 286
1

I don't see a good reason to do this map-filter-map combination. You can use only filter right away:

const unique = array => {
    const set = new Set();
    return array.filter(v => { 
        if (set.has(v.id)) {
            return false
        } else {
            set.add(v.id);
            return true;
        }
    });
};

Also if your array contains the objects that you want to compare by reference, not by their .id, you don't even need to the filtering yourself. You could just write:

const unique = array => Array.from(new Set(array));
Bergi
  • 630,263
  • 148
  • 957
  • 1,375