Javascript search for an object key in a set

2020-03-29 02:43发布

问题:

Is it possible to use the javascript "Set" object to find an element with a certain key? Something like that:

let myObjects = [{"name":"a", "value":0}, {"name":"b", "value":1},{"name":"c", "value":2}];
let mySet = new Set(myObjects);
console.log(mySet.has({"name":"a"}));

回答1:

Not in that way, that would look for the specific object you're passing in, which isn't in the set.

If your starting point is an array of objects, you don't need a Set at all, just Array.prototype.find:

let myObjects = [{"name":"a", "value":0}, {"name":"b", "value":1},{"name":"c", "value":2}];
let found = myObjects.find(e => e.name === "a");
console.log(found);

If you already have a Set and want to search it for a match, you can use its iterator, either directly via for-of:

let myObjects = [{"name":"a", "value":0}, {"name":"b", "value":1},{"name":"c", "value":2}];
let mySet = new Set(myObjects);
let found = undefined; // the `= undefined` is just for emphasis; that's the default value it would have without an initializer
for (const e of mySet) {
  if (e.name === "a") {
    found = e;
    break;
  }
}
console.log(found);

...or indirectly via Array.from to (re)create (the)an array, and then use find:

let myObjects = [{"name":"a", "value":0}, {"name":"b", "value":1},{"name":"c", "value":2}];
let mySet = new Set(myObjects);
let found = Array.from(mySet).find(e => e.name === "a");
console.log(found);


If it's something you need to do often, you might give yourself a utility function for it:

const setFind = (set, cb) => {
  for (const e of set) {
    if (cb(e)) {
      return e;
    }
  }
  return undefined; // undefined` just for emphasis, `return;`
                    // would do effectively th same thing, as
                    // indeed would just not having a `return`
                    // at at all
}

let myObjects = [{"name":"a", "value":0}, {"name":"b", "value":1},{"name":"c", "value":2}];
let mySet = new Set(myObjects);
let found = setFind(mySet, e => e.name === "a");
console.log(found);

You could even put that on Set.prototype (making sure it's non-enumerable), but beware of conflicting with future additions to Set (for instance, I wouldn't be at all surprised if Set.prototype got a find method at some point).



回答2:

You may just want a Set of names:

 let myObjects = [{"name":"a", "value":0}, {"name":"b", "value":1},{"name":"c", "value":2}];

let map = new Set(myObjects.map(el=>el.name));
console.log(map.has("a"));

If you want to get an object by name,thats what a Map is for:

let myObjects = [{"name":"a", "value":0}, {"name":"b", "value":1},{"name":"c", "value":2}];

let map = new Map(myObjects.map(el=>[el.name,el]));
console.log(map.get("a"));


回答3:

If you want to do this with a Set, then the object you're searching for has to be the same object that was added, not an anonymous object.

So, you could achieve what you're looking for if it was set up like this:

let myObject = {"name": "a", "value": 0};
let set = new Set([myObject]);

console.log(set.has(myObject));

This is because set.has() uses SameValueZero() under the hood.

Here is the spec for set.has(): http://www.ecma-international.org/ecma-262/6.0/#sec-set.prototype.has

And for SameValueZero(): http://www.ecma-international.org/ecma-262/6.0/#sec-samevaluezero



回答4:

Short answer - no. Set.has operates on object equality, and each object in the list is unique - so when you pass a new object to .has it will not return true, even if it has the same keys and values.

You could always filter the original list, and if the resulting list has a length greater than zero, your object will be contained within.

const containsObjectWithName = ({ name }) => 
  !!myObjects
    .filter((obj) => obj.name === name)
    .length;

containsObjectWithName({ name: 'a' });  // true


回答5:

Actually a response to Efficiently find an item in a Set but they've closed it as a duplicate #ftw

An alternative approach:


class ArraySet extends Set {

    find(predicate) {
        for(var x of this)
            if (predicate(x)) return x;
        return undefined;
    },

    ... other Array.prototype stuff here

}

const things = new ArraySet([
    { x: 1 },
    { y: 2 }
]);

const found = things.find(item => item.y === 2);