我正在尝试使用新的(ES6)Map
对象来表示属性和值之间的映射。
我有类似以下形式的对象:
Map
我想根据both他们的key1 和 key2值对它们进行分组。
例如,我希望能够按 {key1:value1_1,key2:value2_1},..... {key1:value1_N,key2:value2_N}
和x
分组以下内容:
y
并获得包含以下内容的地图:
[{x:3,y:5,z:3},{x:3,y:4,z:4},{x:3,y:4,z:7},{x:3,y:1,z:1},{x:3,y:5,z:4}]
在Python中,我将使用元组作为字典键。 ES6映射允许将任意对象用作键,但使用标准的相等算法({x:3,y:5} ==> {x:3,y:5,z:3},{x:3,y:5,z:4}
{x:3,y:4} ==> {x:3,y:4,z:4},{x:3,y:4,z:7}
{x:3,y:1} ==> {x:3,y:1,z:1}
),因此根据我的判断,对象仅按引用相等。
我如何使用ES6地图完成这种分组?另外,如果我忽略了一种优雅的方法,那么可以使用普通的JS对象作为解决方案。
我宁愿不使用外部馆藏库,但如果有一个更好的解决方案,我也有兴趣对其进行研究。
[好,我现在在===
上提出了问题,我从Mozilla的esdiscuss中得到了答案:
.equals
,但是为了支持价值对象而被拒绝。 (出于我的充分理由)。[Bradley在ESDiscuss线程上提供了一个基本的这样的集合(概念,不要在生产代码中使用),它可能看起来像这样:
.hashCode
一个更好的解决方案是使用类似function HashMap(hash) {
var map = new Map;
var _set = map.set;
var _get = map.get;
var _has = map.has;
var _delete = map.delete;
map.set = function (k,v) {
return _set.call(map, hash(k), v);
}
map.get = function (k) {
return _get.call(map, hash(k));
}
map.has = function (k) {
return _has.call(map, hash(k));
}
map.delete = function (k) {
return _delete.call(map, hash(k));
}
return map;
}
function TupleMap() {
return new HashMap(function (tuple) {
var keys = Object.keys(tuple).sort();
return keys.map(function (tupleKey) { // hash based on JSON stringification
return JSON.stringify(tupleKey) + JSON.stringify(tuple[tupleKey]);
}).join('\n');
return hashed;
});
}
的名称,它允许指定哈希/等于函数。
您可以查看API文档MontageJS/Collections。
似乎不太方便。你能做什么?一如既往的可怕。
here
还有voilà。
let tuple = (function() {
let map = new Map();
function tuple() {
let current = map;
let args = Object.freeze(Array.prototype.slice.call(arguments));
for (let item of args) {
if (current.has(item)) {
current = current.get(item);
} else {
let next = new Map();
current.set(item, next);
current = next;
}
}
if (!current.final) {
current.final = args;
}
return current.final;
}
return tuple;
})();
Benjamin的答案不适用于所有对象,因为它依赖于JSON.stringify,它无法处理圆形对象,并且可以将不同的对象映射到同一字符串。 Minitech的答案可能会创建巨大的嵌套映射树,我怀疑这在内存和CPU方面都效率低下,特别是对于长元组,因为它必须为元组中的每个元素创建一个映射。
如果知道元组只包含数字,那么最好的解决方案是使用let m = new Map();
m.set(tuple(3, 5), [tuple(3, 5, 3), tuple(3, 5, 4)]);
m.get(tuple(3, 5)); // [[3, 5, 3], [3, 5, 4]]
作为键。如果要使用包含任意对象的元组作为键,则仍然可以使用此方法,但是必须首先将对象映射到唯一标识符。在下面的代码中,我使用[x,y].join(',')
延迟生成这些标识符,该标识符将生成的ID存储在内部映射中。然后,我可以通过串联这些ID来生成元组的键。 (请参阅此答案底部的代码。)
get_object_id
方法然后可用于将对象的元组哈希为一个字符串,该字符串可用作映射中的键。这使用对象等效性:
tuple
如果确定元组将仅包含对象(即不为null,数字或字符串),则可以在x={}; y={};
tuple(x,y) == tuple(x,y) // yields true
tuple(x,x) == tuple(y,y) // yields false
tuple(x,y) == tuple(y,x) // yields false
中使用WeakMap,这样get_object_id
和get_object_id
不会泄漏那些作为参数传递给他们。
tuple
尽管这个问题很老,但是值对象在JavaScript中仍然不存在(所以人们可能仍然很感兴趣),所以我决定编写一个简单的库来完成数组作为键在映射中的相似行为(请参见此处的[ C0])。该库的设计与使用的映射基本相同,只是数组是按元素而不是按标识进行比较。
用法:
var get_object_id = (function() {
var generated_ids = 1;
var map = new Map();
return get_object_id;
function get_object_id(obj) {
if (map.has(obj)) {
return map.get(obj);
} else {
var r = generated_ids++;
map.set(obj, r);
return r;
}
}
})();
function tuple() {
return Array.prototype.map.call(arguments, get_object_id).join(',');
}
// Test
var data = [{x:3,y:5,z:3},{x:3,y:4,z:4},{x:3,y:4,z:7},
{x:3,y:1,z:1},{x:3,y:5,z:4}];
var map = new Map();
for (var i=0; i<data.length; i++) {
var p = data[i];
var t = tuple(p.x,p.y);
if (!map.has(t)) map.set(t,[]);
map.get(t).push(p);
}
function test(p) {
document.writeln((JSON.stringify(p)+' ==> ' +
JSON.stringify(map.get(tuple(p.x,p.y)))).replace(/"/g,''));
}
document.writeln('<pre>');
test({x:3,y:5});
test({x:3,y:4});
test({x:3,y:1});
document.writeln('</pre>');
警告:但是该库通过标识来对待关键元素,因此以下操作不起作用:
https://github.com/Jamesernator/es6-array-map
但是,只要您可以将数据序列化为基元数组,就可以按如下方式使用ArrayMap:
var map = new ArrayMap();
map.set([1,2,3], 12);
map.get([1,2,3]); // 12
map.set(['cats', 'hats'], {potatoes: 20});
map.get(['cats', 'hats']); // {potatoes: 20}
已经过去了很多年,这仍然是JavaScript的问题。我改进了Jamesernator的方法,并创建了程序包var map = new ArrayMap();
map.set([{x: 3, y: 5}], {x:3, y:5, z:10});
map.get([{x: 3, y: 5}]); // undefined as objects within the list are
// treated by identity
。现在您可以得到想要的东西:
var serialize = function(point) {
return [point.x, point.y];
};
var map = new ArrayMap(null, serialize);
map.set({x: 10, y: 20}, {x: 10, y: 20, z: 30});
map.get({x: 10, y: 20}); // {x: 10, y: 20, z: 30}