可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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);