用JavaScript创建Set的方法?


82

在Eloquent JavaScript,第4章中,通过创建对象并将值存储为属性名称,然后将任意值(例如true)分配为属性值来创建一组值。要检查该值是否已包含在集合中,请使用in运算符:

var set = {};

if (!'Tom' in set) { 
  set.Tom = true;
}

这是惯用的JavaScript吗?会不会使用更好的数组?

var set = [];

if (!'Tom' in set) { 
  set.push = 'Tom';
}

1
您如何期望一个'Tom' in set看起来像真的数组?看起来很像你对某件事有错误的假设,我正在试图找出什么

10
仅供参考,您需要使用括号:if(!('Tom' in set))。目前表示false in set!'Tom' === false
pimvdb

ES6已经设置好了,请看下面约翰的回答
Ben Taliadoros

Answers:


101

集合现在在ES2015中可用(又名ES6,即ECMAScript 6)。自2015年6月起,ES6已成为JavaScript的当前标准。

ECMAScript 6具有数据结构Set,该数据结构适用于任意值,快速且可以正确处理NaN。-阿克塞尔Rauschmayer先生探索ES6

Axel Rauschmayer的Exploring ES6的前两个示例:

管理单个元素:

> let set = new Set();
> set.add('red')

> set.has('red')
true
> set.delete('red')
true
> set.has('red')
false

确定集合的大小并清除它:

> let set = new Set();
> set.add('red')
> set.add('green')

> set.size
2
> set.clear();
> set.size
0

如果您想了解有关JavaScript中的Set的更多信息,请查看Exploring ES6。这本书是可以在线免费阅读的,但是如果您想支持作者Axel Rauschmayer博士,则可以30美元左右的价格购买该书。

如果现在要使用Sets和ES6 ,则可以使用Babel,ES6到ES5编译器及其polyfills。

编辑:截至2017年6月6日,大多数主要浏览器在其最新版本(IE 11除外)中都具有完整的Set支持。这意味着如果您不希望支持较旧的浏览器,则可能不需要Babel。如果要查看包括当前浏览器在内的其他浏览器的兼容性,请查看Kangax的ES6兼容性表

编辑:

只是澄清初始化。集可以在其构造函数中采用任何同步迭代。这意味着它们不仅可以接受数组,还可以接受字符串和迭代器。例如,以下一组数组和字符串初始化:

const set1 = new Set(['a','a','b','b','c','c']);
console.log(...set1);
console.log(set1.size);
const set2 = new Set("aabbcc");
console.log(...set2);
console.log(set2.size);

数组和字符串的两个输出是相同的。注意,这...set1传播语法。似乎将iterable的每个元素都逐一添加到集合中,因此,由于数组和字符串具有相同的元素,并且由于元素的顺序相同,因此创建的集合也相同。关于集合要注意的另一件事是,在对它们进行迭代时,迭代顺序遵循将元素插入到集合中的顺序。这是一个遍历集合的示例:

const set1 = new Set(['a','a','b','b','c','c']);
for(const element of set1) {
  console.log(element);
}

由于您可以使用任何可迭代的方法来初始化集合,因此甚至可以使用生成器函数中的迭代。这是两个产生相同输出的迭代器初始化的示例:

// a simple generator example
function* getLetters1 () {
  yield 'a';
  yield 'a';
  yield 'b';
  yield 'b';
  yield 'c';
  yield 'c';
}

// a somewhat more commonplace generator example
// with the same output as getLetters1.
function* getLetters2 (letters, repeatTimes) {
  for(const letter of letters) {
    for(let i = 0; i < repeatTimes; ++i) { 
      yield letter;
    }
  }
}

console.log("------ getLetters1 ------");
console.log(...getLetters1());
const set3 = new Set(getLetters1());
console.log(...set3);
console.log(set3.size);

console.log("------ getLetters2 ------");
console.log(...getLetters2('abc', 2));
const set4 = new Set(getLetters2('abc', 2));
console.log(...set4);
console.log(set4.size);

这些示例的生成器函数可以写为不重复,但是如果生成器函数更复杂,并且只要以下内容不会对性能产生负面影响,则可以使用Set方法帮助仅从生成器的值中获取不再重复。

如果您想了解更多有关集合的知识,而无需阅读Rauschmayer博士在其书中的章节,则可以查看Set上MDN文档。MDN还具有在一组迭代的更多的例子,例如使用forEach和使用.keys.values以及.entries方法。MDN还具有示例,例如集合并集,集合相交,集合差,对称集合差和集合超集检查。希望其中的大多数操作都可以在JavaScript中使用,而无需构建自己的函数来支持它们。实际上,这个TC39提案提出了新的Set方法,如果该提案达到了第4阶段,则应该在将来的某个时间点将以下方法添加到JavaScript中的Set中:

  • Set.prototype.intersection(iterable)-方法通过设置交集操作创建新的Set实例。
  • Set.prototype.union(iterable)-方法通过集合联合操作创建新的Set实例。
  • Set.prototype.difference(iterable)-方法创建新的Set,而iterable中没有元素。
  • Set.prototype.symmetricDifference(iterable)-返回仅在this或inerable中找到的元素集。
  • Set.prototype.isSubsetOf(iterable)
  • Set.prototype.isDisjointFrom(iterable)
  • Set.prototype.isSupersetOf(可迭代)

Set本身也是可迭代的,因此您可以使用其他集合初始化集合以进行复制,或者对联合操作执行诸如new Set([... setA,... setB])之类的操作。
约翰

1
@LanceKind我添加了一些其他信息,包括显示您可以使用任何可迭代的集合初始化数组,而不仅仅是数组。
约翰

32

我使用dict对象作为集合。这适用于字符串和数字,但是如果您想使用自定义相等和比较运算符来拥有一组对象,我想会引起问题:

创建集合:

var example_set = 
{
    'a':true,
    'b':true,
    'c':true
}

测试是否包含在集合中

if( example_set['a'] ){
    alert('"a" is in set');
}

向集合添加元素

example_set['d'] = true;

从集合中删除元素

delete example_set['a'];


1
我正在处理的代码使用的是较旧版本的引擎,但不支持Set。这有帮助。
阿比吉斯(Abhijith Madhav)2016年

16

集不允许重复的条目,通常也不保证预定义的顺序。数组同时执行这两个操作,因此违反了集合的含义(除非您进行其他检查)。


2
数组不会过滤重复的条目...例如arr.push({id:1,name:“ Jake”})就像Objective-C中的NSSet :)
iTux

如果您接受mot-a-mot,不会。但是您可以只使用id作为数组(映射)键。 arr[id] = {"name": "Jake"};
布法罗


9

如果要从数组创建集合,只需执行以下操作:

let arr = [1, 1, 2, 1, 3];
let mySet = new Set(arr); // Set { 1, 2, 3 }

这是我在Python编程时非常喜欢的一种糖语法,很高兴ES6最终使做相同的事情成为可能。

注意:然后我意识到我说的话并没有直接回答您的问题。在ES5中出现这种“ hack”的原因是因为通过键在对象中的查找时间(O(1))比在数组(O(n))中的查找时间明显更快。在对性能至关重要的应用程序中,您可以牺牲一点可读性或直觉来获得更好的性能。

但是,欢迎来到2017年,您现在可以在所有主流现代浏览器中使用适当的Set


6

ES6/中设置ES2015

ES6/ES2015现在已经内置了。集合是一种数据结构,它允许存储任何类型的唯一值,无论是原始值还是对象引用。可以使用ES6内置set构造函数以以下方式声明一个set :

const set = new Set([1, 2, 3, 4, 5]);

使用Set构造函数创建集合时,我们新创建的set对象将从继承Set.prototype。这具有各种辅助方法和属性。这使您可以轻松地执行以下操作:

例:

const set = new Set([1, 2, 3, 4, 5]);

// checkout the size of the set
console.log('size is: ' + set.size);

// has method returns a boolean, true if the item is in the set
console.log(set.has(1));

// add a number
set.add(6);

// delete a number
set.delete(1);

// iterate over each element using a callback
set.forEach((el) => {
  console.log(el);
});

// remove all the entries from the set
set.clear();

浏览器兼容性:

除了缺少某些功能的IE之外,所有主流浏览器现在都完全支持集。有关确切的参考,请参阅mdn docs


@Velojet:好的,很公平。
kjhughes

3

使用裸露的javascript对象来模拟集合有两个问题:首先,一个对象可以具有继承的属性,该属性会拧入“ in”运算符;其次,您只能以这种方式存储标量值,而使一组对象不是可能。因此,集的现实实现应提供方法addcontains而不应提供简单的in属性分配。


@kojiro:当用作键时,对象会转换为字符串:set={};set[{x:1}]=123;alert(set[{z:99}])
georg

3

您可以尝试Buckets,它是一个javascript数据结构库,具有处理集所需的一切。


它提供一个addAll(array)方法吗?
jorrebor

1

Set对象的基本创建和使用

let mySet = new Set()

mySet.add(2)         // Set {2}
mySet.add(7)         // Set {2, 7}
mySet.add(7)         // Set {2, 7}
mySet.add('my text') // Set {2, 7, 'my text'}
let myObj = { a: 1, b: 2 }
mySet.add(myObj)     // Set {2, 7, 'my text', {...}}
mySet.has(2)         // true
mySet.has(myObj)     // true
mySet.size           // 4

迭代

for (let item of mySet) console.log(item)  // 2, 7, 'my text', {a:1, b:2}
mySet.forEach(value => console.log(value)) // 2, 7, 'my text', {a:1, b:2}

转换为数组

var myArr = Array.from(mySet)             // [2, 7, 'my text', {a:1, b:2}]

Set Set提供的最独特的功能是Set对象中的每个值都必须是唯一的。因此,您不能添加重复的值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.