在 ES6 Set 中存储数组并按值访问它们
Storing arrays in ES6 Set and accessing them by value
有没有一种简单的方法可以验证 ES6 Set 包含的值是特定数组?我想要一个不需要我使用参考的解决方案:
var set = new Set();
var array = [1, 2];
set.add(array);
set.has(array); // true
set.add([3, 4]);
set.has([3, 4]); // false
到目前为止,我的解决方案是将所有内容存储为字符串,但这很烦人:
set.add([3, 4].toString());
set.has([3, 4].toString()); // true
没有。
A Set
适用于对象和基元,可用于防止相同的基元和重新添加相同的对象实例。
每个数组都是它们自己的对象,因此您实际上可以添加两个具有相同值的不同数组。
var set = new Set();
set.add([3, 4]);
set.add([3, 4]);
console.log(set.size);//2
此外,没有什么可以阻止对象在集合中被更改一次。
var set = new Set();
var a1 = [3, 4];
var a2 = [3, 4];
set.add(a1);
set.add(a2);
a2.push(5);
for (let a of set) {
console.log(a);
}
//Outputs:
// [3, 4]
// [3, 4, 5]
集合 用于检查集合中对象的值。由于一个对象的值随时都可能改变,所以它不会比简单地自己循环它们更有效。
您正在寻找的功能已在各种 ECMAScript 提案中被提出,但它似乎不会很快出现。
显然,set.has()
不会像那样检查数组元素。但是,它们已添加:
var set = new Set([1, 2]);
set.add([3, 4]);
console.log(Array.from(set));
它没有使用 Set
,但可能会解决您的问题。
如果你想确保一个数组只能在一个集合中存在一次,并且你想要即时查找,你可以通过滥用它的键来使用散列。他们不一定需要一个值。您可以将它们设置为 undefined
、true
或其他能让您晚上睡得更好的设置。
灵感来自:javascript search array of arrays
做一个包装纸是半途而废,但它也足够简单来处理一个小工作。
const hash = {};
hash[[1, 2, 3]] = undefined;
hash[[3, 4]] = undefined;
hash[[3, 4]] = undefined;
console.log({hash});
console.log("hash has [3, 4]?", hash.hasOwnProperty([3, 4]));
对我来说,这会很好,因为我正在检查集合中是否存在坐标。不幸的是,Whosebug
似乎 Set
不能解决这个问题。
巧合的是,它似乎隐含地解决了您不在数组上调用 toString
的问题,这似乎是在幕后进行的。我个人不喜欢 hash.hasOwnProperty
.
更优雅的选择
您甚至可以为您的代码编写一个包装器,例如:
class ArraySet extends Set {
add(arr) {
super.add(arr.toString());
}
has(arr) {
return super.has(arr.toString());
}
}
const arraySet = new ArraySet();
arraySet.add([1, 2]);
arraySet.add([3, 4]);
arraySet.add([3, 4]);
console.log("ArraySet has [3, 4]?", arraySet.has([3, 4]));
代表太少,无法添加评论,所以我将其添加为此处的答案。
预先为文字墙道歉。
我喜欢CTS_AE的回答,也想走同样的路。
但是,需要注意一件事,那就是如何将数组放入 String。
let memory = {};
memory[[1,2,3]] = "123";
console.log(memory[[1,2,3]]); // "123"
console.log([1,2,3].toString(); // "1,2,3"
console.log(memory["1,2,3"]); // "123"
如果您知道您放入的只是数组,那么这就不是问题了……或者会吗?
MDN 关于 Array.prototype.toString() 说
For Array objects, the toString method joins the array and returns one string containing each array element separated by commas.
这带来了 2 个大问题:
- 通过
array
建立索引与通过 array.toSring()
建立索引相同
- 由于
toString()
是递归的,嵌套数组最终被字符串化为与单级数组相同的格式。
let memory = {};
memory[[1,2,3]] = "123";
console.log(memory[[1,2,3]]); // "123"
console.log(memory["1,2,3"]); // "123"
console.log(memory[[[1],[2],[3]]]); // "123"
console.log(memory[[[[1],2],3]]); // "123"
...并且列表还在继续。不难看出这些问题何时会真正破坏您的项目。我刚才在尝试 memoizing
时遇到了这样的问题
function doSomeStuff(string, sourceIdxs, targetIdxs) {
if (memo[[string, sourceIdxs, targetIdxs]])
return memo[[string, sourceIdxs, targetIdxs]];
// ...
}
在这种情况下,例如 ["foo", [1, 3, 5], [6, 10]]
和 ["foo", [1, 3], [5, 6, 10]]
指向相同的值,我最终覆盖了现有值,有效地破坏了函数内存。
现在,在上述 ArraySet
答案的具体情况下,问题仍然存在。虽然您不介意用另一个“相同”密钥覆盖现有密钥,但最终可能会出现误报。
那么如何解决这个问题?
选项 1.stringify
简单的方法是使用 JSON.stringify()
编写所有关键数据的“精确”字符串表示。
let memory = {};
memory[JSON.stringify([1,2,3])] = "123";
console.log(memory[JSON.stringify([1,2,3])]); // "123"
console.log(memory[JSON.stringify([[1],[2,3]])); // undefined
这有助于误报....有点。首先,您不会遇到数组中重叠元素的问题。 [[1,2],[3]]
不再指向 [[1],[2,3]]
所在的位置。
但是,[1,2,3]
和 "[1,2,3]"
可以。此外(在某些奇怪的情况下),数组元素可能包含 [
或 ]
字符,这可能会使问题进一步复杂化。
而且您可能不关心这种情况。您的钥匙可能仍会受到足够的约束,以免发生此类事情。你甚至可能想要这样的行为。如果你这样做,那就去吧。
好:
- 修复了
ArraySet
的大部分问题
- 易于实施
差:
JSON.stringify()
相当慢。
选项 2.分隔符
更简单、更快捷,同时与旧解决方案相比仍具有一些优势。
function toSepString(arr) { return arr.join("|") }
let memory = {};
memory[toSepString([[1,2],3])] = "123";
console.log(memory[toSepString([1,[2,3]])]); // undefined
现在当然这只对“最外层”有帮助。
console.log(toSepString([1,[2,[3]]])); // "1|2,3"
console.log(toSepString([1,[[2],[[3]]]]); // "1|2,3"
所以如果你使用这个,你需要确保你的键数组的特定元素在转换为字符串时不会变得模糊。
当然,您可以使函数递归并在每个开始和结束处添加 "[", "]"
,实质上是复制 JSON.stringify()
的一部分功能。我想这会比直接调用 JSON.stringify()
更高效,但这需要测试。
好:
- 比其内置的对应物更快
差:
- 嵌套数组的问题
- 分隔符未出现在值中的问题
使用对象键作为一组的 Janky 解决方案:
let set = {};
set[[1,4]] = true;
set[[4,5]] = true;
set[[4,5]] = true; // Already added, won't make a difference
console.log(set); // "{ '1,4': true, '4,5': true }"
let setAsArr = (Object.keys(set).map(a => a.split(",")));
console.log(setAsArr) // [ [ '1', '4' ], [ '4', '5' ] ]
这样您就可以创建一个“集合”来防止重复的数组项条目并且可以轻松访问项。
注意: 不适用于包含“,”字符的字符串数组
有没有一种简单的方法可以验证 ES6 Set 包含的值是特定数组?我想要一个不需要我使用参考的解决方案:
var set = new Set();
var array = [1, 2];
set.add(array);
set.has(array); // true
set.add([3, 4]);
set.has([3, 4]); // false
到目前为止,我的解决方案是将所有内容存储为字符串,但这很烦人:
set.add([3, 4].toString());
set.has([3, 4].toString()); // true
没有。
A Set
适用于对象和基元,可用于防止相同的基元和重新添加相同的对象实例。
每个数组都是它们自己的对象,因此您实际上可以添加两个具有相同值的不同数组。
var set = new Set();
set.add([3, 4]);
set.add([3, 4]);
console.log(set.size);//2
此外,没有什么可以阻止对象在集合中被更改一次。
var set = new Set();
var a1 = [3, 4];
var a2 = [3, 4];
set.add(a1);
set.add(a2);
a2.push(5);
for (let a of set) {
console.log(a);
}
//Outputs:
// [3, 4]
// [3, 4, 5]
集合
您正在寻找的功能已在各种 ECMAScript 提案中被提出,但它似乎不会很快出现。
显然,set.has()
不会像那样检查数组元素。但是,它们已添加:
var set = new Set([1, 2]);
set.add([3, 4]);
console.log(Array.from(set));
它没有使用 Set
,但可能会解决您的问题。
如果你想确保一个数组只能在一个集合中存在一次,并且你想要即时查找,你可以通过滥用它的键来使用散列。他们不一定需要一个值。您可以将它们设置为 undefined
、true
或其他能让您晚上睡得更好的设置。
灵感来自:javascript search array of arrays
做一个包装纸是半途而废,但它也足够简单来处理一个小工作。
const hash = {};
hash[[1, 2, 3]] = undefined;
hash[[3, 4]] = undefined;
hash[[3, 4]] = undefined;
console.log({hash});
console.log("hash has [3, 4]?", hash.hasOwnProperty([3, 4]));
对我来说,这会很好,因为我正在检查集合中是否存在坐标。不幸的是,Whosebug
似乎 Set
不能解决这个问题。
巧合的是,它似乎隐含地解决了您不在数组上调用 toString
的问题,这似乎是在幕后进行的。我个人不喜欢 hash.hasOwnProperty
.
更优雅的选择
您甚至可以为您的代码编写一个包装器,例如:
class ArraySet extends Set {
add(arr) {
super.add(arr.toString());
}
has(arr) {
return super.has(arr.toString());
}
}
const arraySet = new ArraySet();
arraySet.add([1, 2]);
arraySet.add([3, 4]);
arraySet.add([3, 4]);
console.log("ArraySet has [3, 4]?", arraySet.has([3, 4]));
代表太少,无法添加评论,所以我将其添加为此处的答案。 预先为文字墙道歉。
我喜欢CTS_AE的回答,也想走同样的路。 但是,需要注意一件事,那就是如何将数组放入 String。
let memory = {};
memory[[1,2,3]] = "123";
console.log(memory[[1,2,3]]); // "123"
console.log([1,2,3].toString(); // "1,2,3"
console.log(memory["1,2,3"]); // "123"
如果您知道您放入的只是数组,那么这就不是问题了……或者会吗?
MDN 关于 Array.prototype.toString() 说
For Array objects, the toString method joins the array and returns one string containing each array element separated by commas.
这带来了 2 个大问题:
- 通过
array
建立索引与通过array.toSring()
建立索引相同
- 由于
toString()
是递归的,嵌套数组最终被字符串化为与单级数组相同的格式。
let memory = {};
memory[[1,2,3]] = "123";
console.log(memory[[1,2,3]]); // "123"
console.log(memory["1,2,3"]); // "123"
console.log(memory[[[1],[2],[3]]]); // "123"
console.log(memory[[[[1],2],3]]); // "123"
...并且列表还在继续。不难看出这些问题何时会真正破坏您的项目。我刚才在尝试 memoizing
时遇到了这样的问题function doSomeStuff(string, sourceIdxs, targetIdxs) {
if (memo[[string, sourceIdxs, targetIdxs]])
return memo[[string, sourceIdxs, targetIdxs]];
// ...
}
在这种情况下,例如 ["foo", [1, 3, 5], [6, 10]]
和 ["foo", [1, 3], [5, 6, 10]]
指向相同的值,我最终覆盖了现有值,有效地破坏了函数内存。
现在,在上述 ArraySet
答案的具体情况下,问题仍然存在。虽然您不介意用另一个“相同”密钥覆盖现有密钥,但最终可能会出现误报。
那么如何解决这个问题?
选项 1.stringify
简单的方法是使用 JSON.stringify()
编写所有关键数据的“精确”字符串表示。
let memory = {};
memory[JSON.stringify([1,2,3])] = "123";
console.log(memory[JSON.stringify([1,2,3])]); // "123"
console.log(memory[JSON.stringify([[1],[2,3]])); // undefined
这有助于误报....有点。首先,您不会遇到数组中重叠元素的问题。 [[1,2],[3]]
不再指向 [[1],[2,3]]
所在的位置。
但是,[1,2,3]
和 "[1,2,3]"
可以。此外(在某些奇怪的情况下),数组元素可能包含 [
或 ]
字符,这可能会使问题进一步复杂化。
而且您可能不关心这种情况。您的钥匙可能仍会受到足够的约束,以免发生此类事情。你甚至可能想要这样的行为。如果你这样做,那就去吧。
好:
- 修复了
ArraySet
的大部分问题
- 易于实施
差:
JSON.stringify()
相当慢。
选项 2.分隔符
更简单、更快捷,同时与旧解决方案相比仍具有一些优势。
function toSepString(arr) { return arr.join("|") }
let memory = {};
memory[toSepString([[1,2],3])] = "123";
console.log(memory[toSepString([1,[2,3]])]); // undefined
现在当然这只对“最外层”有帮助。
console.log(toSepString([1,[2,[3]]])); // "1|2,3"
console.log(toSepString([1,[[2],[[3]]]]); // "1|2,3"
所以如果你使用这个,你需要确保你的键数组的特定元素在转换为字符串时不会变得模糊。
当然,您可以使函数递归并在每个开始和结束处添加 "[", "]"
,实质上是复制 JSON.stringify()
的一部分功能。我想这会比直接调用 JSON.stringify()
更高效,但这需要测试。
好:
- 比其内置的对应物更快
差:
- 嵌套数组的问题
- 分隔符未出现在值中的问题
使用对象键作为一组的 Janky 解决方案:
let set = {};
set[[1,4]] = true;
set[[4,5]] = true;
set[[4,5]] = true; // Already added, won't make a difference
console.log(set); // "{ '1,4': true, '4,5': true }"
let setAsArr = (Object.keys(set).map(a => a.split(",")));
console.log(setAsArr) // [ [ '1', '4' ], [ '4', '5' ] ]
这样您就可以创建一个“集合”来防止重复的数组项条目并且可以轻松访问项。
注意: 不适用于包含“,”字符的字符串数组