在一个集合中搜索对象的键的Javascript方法

13

是否可以使用Javascript中的“Set”对象来查找具有特定键的元素?类似这样:

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"}));
7个回答

27

不是那样,那将搜索您传递的特定对象,而该对象不在集合中。

如果您的起点是一个对象数组,则根本不需要Set,只需使用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);

如果您已经拥有一个 Set 并想要在其中搜索匹配项,您可以使用它的迭代器,直接通过 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);

通过Array.from直接或间接地重新创建数组,然后使用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);

如果这是你需要经常做的事情,你可以为自己创建一个实用函数:

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

你甚至可以将它放在Set.prototype上(确保它是不可枚举的),但要注意与将来添加到Set的内容冲突(例如,如果Set.prototype在某个时候获得了一个find方法,我一点也不惊讶)。


1
谢谢您的回答,我的问题只是出于好奇,因为我在某个地方读到,在Set对象中搜索的复杂度为O(1)。 - Alexandre Senges
检查某物是否在其中!== 不等于搜索。 - Jonas Wilms
@Jonasw:没错!Alexandre,如果你只需要一个布尔值结果,那么使用some而不是上面的find(并相应地调整for-of)。 - T.J. Crowder
1
@AlexandreSenges 如果你有原始项目,那么搜索集合的时间复杂度为O(1)。对于基元(new Set([1, 2, 3])),你不需要原始项目,因为2 === 2,但是对于对象,你不能这样做,因为{} !== {}。如果没有原始对象的引用,你无法在O(1)的时间复杂度内查找集合中任意对象的任意属性...此时你不需要在集合中查找它。 - VLAZ

7
您可能只需要一个名称集合:
 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"));

如果您想通过名称获取对象,那么 Map 就是为此而设计的:

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

1
虽然 Map 可以工作,但这就是 set 的用途:let mySet = new Set(myObjects.map(e => e.name)); console.log(mySet.has("a"));(如果我们要用这种方式解决它)。 - T.J. Crowder

2

如果您想使用Set来实现这个功能,那么您要查找的对象必须是添加的相同对象,而不是匿名对象。

因此,如果设置如下,您可以实现您所需的功能:

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

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

这是因为set.has()在底层使用了SameValueZero()
这里是set.has()的规范: http://www.ecma-international.org/ecma-262/6.0/#sec-set.prototype.has
这里是SameValueZero()的规范: http://www.ecma-international.org/ecma-262/6.0/#sec-samevaluezero

这并不是OP所要求的。问题很明显有两个不同的对象(一个在数组中,另一个被搜索),而不是单个对象。 - T.J. Crowder

1
简短回答-不行。`Set.has`基于对象相等性进行操作,列表中的每个对象都是唯一的,因此当您将新对象传递给`.has`时,即使具有相同的键和值,它也不会返回`true`。您始终可以过滤原始列表,如果结果列表的长度大于零,则您的对象将被包含在其中。
const containsObjectWithName = ({ name }) => 
  !!myObjects
    .filter((obj) => obj.name === name)
    .length;

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

1
      var myObj = { "A": "1", "B": "2", "C": "3" }
      var objLength = Object.keys(myObj).length

      var firstKeyOfObject = Object.keys(myObj)[0]
      var firstValueOfObject = myObj[Object.keys(myObj)[0]]
      var lastKeyOfObject = Object.keys(myObj)[objLength - 1]
      var lastValueOfObject = myObj[Object.keys(myObj)[objLength - 1]]
      

      console.log('firstKeyOfObject', firstKeyOfObject)
      console.log('firstValueOfObject', firstValueOfObject)
      console.log('lastKeyOfObject', lastKeyOfObject)
      console.log('lastValueOfObject', lastValueOfObject)

1

实际上是对如何高效地在Set中查找元素的回复,但他们将其关闭了,标记为重复问题#ftw

另一种方法:


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


0

如果您希望您的解决方案具有高性能,请使用Map而不是Set。 如果您的Set不是很大,您可以采用T.J. Crowder solution的方法,将Set转换为Array,然后进行筛选,反之亦然。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接