我在使用JavaScript。我想存储一个唯一、无序的字符串值列表,具有以下属性:
- 快速查询'A'是否在列表中?
- 快速删除列表中存在的'A'
- 如果不存在,则快速将'A'添加到列表中。
我真正想要的是一个集合。有没有关于如何在JavaScript中模拟集合的最佳建议?
这个问题建议使用对象,其中键存储属性,而所有值都设置为true:这是一个明智的做法吗?
我在使用JavaScript。我想存储一个唯一、无序的字符串值列表,具有以下属性:
我真正想要的是一个集合。有没有关于如何在JavaScript中模拟集合的最佳建议?
这个问题建议使用对象,其中键存储属性,而所有值都设置为true:这是一个明智的做法吗?
Set
对象。它具有非常好的功能,可以直接在您的环境中使用。
obj
是您要操作的对象,A
是一个具有所需值的变量,则可以执行以下操作:// create empty object
var obj = {};
// or create an object with some items already in it
var obj = {"1":true, "2":true, "3":true, "9":true};
问题1:在列表中是否存在A
:
if (A in obj) {
// put code here
}
问题2:如果列表中存在'A',则将其删除:
delete obj[A];
问题3:如果列表中没有'A',则将其添加到列表中
obj[A] = true;
为了完整起见,检查A
是否在列表中的测试更加安全:
if (Object.prototype.hasOwnProperty.call(obj, A))
// put code here
}
由于基本对象(如constructor
属性)上的内置方法和/或属性可能存在冲突,因此需要注意。
Set
的行以查看当前浏览器可用性的状态。 for(item of mySet)
之类的操作,它会自动迭代整个集合。 但是,这种语言特性无法通过polyfill实现。 您仍然可以在不使用新的ES6语言功能的情况下迭代ES6 set,但是没有新语言功能,使用起来不如我在下面包含的另一个set接口方便。 .add()
方法调用,而我的polyfill则支持。 这可能是规范的问题,因为它尚未最终确定。
这里是miniSet的代码副本(最新代码在github上)。
"use strict";
//-------------------------------------------
// Simple implementation of a Set in javascript
//
// Supports any element type that can uniquely be identified
// with its string conversion (e.g. toString() operator).
// This includes strings, numbers, dates, etc...
// It does not include objects or arrays though
// one could implement a toString() operator
// on an object that would uniquely identify
// the object.
//
// Uses a javascript object to hold the Set
//
// This is a subset of the Set object designed to be smaller and faster, but
// not as extensible. This implementation should not be mixed with the Set object
// as in don't pass a miniSet to a Set constructor or vice versa. Both can exist and be
// used separately in the same project, though if you want the features of the other
// sets, then you should probably just include them and not include miniSet as it's
// really designed for someone who just wants the smallest amount of code to get
// a Set interface.
//
// s.add(key) // adds a key to the Set (if it doesn't already exist)
// s.add(key1, key2, key3) // adds multiple keys
// s.add([key1, key2, key3]) // adds multiple keys
// s.add(otherSet) // adds another Set to this Set
// s.add(arrayLikeObject) // adds anything that a subclass returns true on _isPseudoArray()
// s.remove(key) // removes a key from the Set
// s.remove(["a", "b"]); // removes all keys in the passed in array
// s.remove("a", "b", ["first", "second"]); // removes all keys specified
// s.has(key) // returns true/false if key exists in the Set
// s.isEmpty() // returns true/false for whether Set is empty
// s.keys() // returns an array of keys in the Set
// s.clear() // clears all data from the Set
// s.each(fn) // iterate over all items in the Set (return this for method chaining)
//
// All methods return the object for use in chaining except when the point
// of the method is to return a specific value (such as .keys() or .isEmpty())
//-------------------------------------------
// polyfill for Array.isArray
if(!Array.isArray) {
Array.isArray = function (vArg) {
return Object.prototype.toString.call(vArg) === "[object Array]";
};
}
function MiniSet(initialData) {
// Usage:
// new MiniSet()
// new MiniSet(1,2,3,4,5)
// new MiniSet(["1", "2", "3", "4", "5"])
// new MiniSet(otherSet)
// new MiniSet(otherSet1, otherSet2, ...)
this.data = {};
this.add.apply(this, arguments);
}
MiniSet.prototype = {
// usage:
// add(key)
// add([key1, key2, key3])
// add(otherSet)
// add(key1, [key2, key3, key4], otherSet)
// add supports the EXACT same arguments as the constructor
add: function() {
var key;
for (var i = 0; i < arguments.length; i++) {
key = arguments[i];
if (Array.isArray(key)) {
for (var j = 0; j < key.length; j++) {
this.data[key[j]] = key[j];
}
} else if (key instanceof MiniSet) {
var self = this;
key.each(function(val, key) {
self.data[key] = val;
});
} else {
// just a key, so add it
this.data[key] = key;
}
}
return this;
},
// private: to remove a single item
// does not have all the argument flexibility that remove does
_removeItem: function(key) {
delete this.data[key];
},
// usage:
// remove(key)
// remove(key1, key2, key3)
// remove([key1, key2, key3])
remove: function(key) {
// can be one or more args
// each arg can be a string key or an array of string keys
var item;
for (var j = 0; j < arguments.length; j++) {
item = arguments[j];
if (Array.isArray(item)) {
// must be an array of keys
for (var i = 0; i < item.length; i++) {
this._removeItem(item[i]);
}
} else {
this._removeItem(item);
}
}
return this;
},
// returns true/false on whether the key exists
has: function(key) {
return Object.prototype.hasOwnProperty.call(this.data, key);
},
// tells you if the Set is empty or not
isEmpty: function() {
for (var key in this.data) {
if (this.has(key)) {
return false;
}
}
return true;
},
// returns an array of all keys in the Set
// returns the original key (not the string converted form)
keys: function() {
var results = [];
this.each(function(data) {
results.push(data);
});
return results;
},
// clears the Set
clear: function() {
this.data = {};
return this;
},
// iterate over all elements in the Set until callback returns false
// myCallback(key) is the callback form
// If the callback returns false, then the iteration is stopped
// returns the Set to allow method chaining
each: function(fn) {
this.eachReturn(fn);
return this;
},
// iterate all elements until callback returns false
// myCallback(key) is the callback form
// returns false if iteration was stopped
// returns true if iteration completed
eachReturn: function(fn) {
for (var key in this.data) {
if (this.has(key)) {
if (fn.call(this, this.data[key], key) === false) {
return false;
}
}
}
return true;
}
};
MiniSet.prototype.constructor = MiniSet;
Object.keys(obj)
。 - Blixtobj.hasOwnProperty(prop)
。而是使用 Object.prototype.hasOwnProperty.call(obj, prop)
,即使“集合”包含值 "hasOwnProperty"
也可以正常工作。 - davidchambers您可以创建一个没有属性的对象,如下:
var set = Object.create(null)
该对象可用作集合,并且无需使用hasOwnProperty
。
var set = Object.create(null); // create an object with no properties
if (A in set) { // 1. is A in the list
// some code
}
delete set[a]; // 2. delete A from the list if it exists in the list
set[A] = true; // 3. add A to the list if it is not already present
set = {}
,它会继承Object的所有属性(例如toString
),因此您需要在if(A in set)
中使用hasOwnProperty
检查集合的有效负载(即您添加的属性)。 - Thorben Croiséset[A]=true
语句,而不仅仅是一个初始化程序? - vogomatixs = Object.create(null);s["thorben"] = true;ss = Object.create(s)
的操作。 - Thorben Croisé' ' in new Set(['\t', ' '])
产生 false
,同样地,1 in new Set([1,2])
也会产生 false
。 - Valin
不起作用是因为 Set
对象没有将其元素作为属性,这是不好的,因为集合可以有任何类型的元素,但属性必须是字符串。您可以使用 has
:Set([1,2]).has(1)
。 - Oriol在JavaScript的ES6版本中,你可以使用内置类型set(检查兼容性)。
var numbers = new Set([1, 2, 4]); // Set {1, 2, 4}
要向集合中添加元素,你只需使用.add()
,它在O(1)
的时间内运行,如果元素不存在,则将其添加到集合中,如果已存在,则不执行任何操作。您可以向其中添加任何类型的元素(数组、字符串、数字等)。
numbers.add(4); // Set {1, 2, 4}
numbers.add(6); // Set {1, 2, 4, 6}
要检查集合中元素的数量,您可以简单地使用.size
。 这也在O(1)
时间内运行。
numbers.size; // 4
使用.delete()
从集合中删除元素。如果该值存在(并且已被删除),则它返回true,如果该值不存在,则返回false。同时,运行时间复杂度为O(1)
。
numbers.delete(2); // true
numbers.delete(2); // false
使用.has()
来检查集合中是否存在元素,如果元素在集合中则返回true,否则返回false。时间复杂度为O(1)
。
numbers.has(3); // false
numbers.has(1); // true
除了你想要的方法,还有几个额外的方法:
numbers.clear();
只会从集合中删除所有元素numbers.forEach(callback);
以插入顺序迭代集合中的值numbers.entries();
创建所有值的迭代器numbers.keys();
返回集合的键,与 numbers.values()
相同另外还有一个 WeakSet,只允许添加对象类型的值。
.add()
在 O(1) 时间内运行的参考吗?我对此很感兴趣。 - Green对这个集合中的每个值调用指定的函数,并将该值作为参数传递。该函数的上下文是这个集合。返回未定义。迭代顺序是任意的。