javascript中的一组对象
Set of objects in javascript
我想在Javascript中有一组对象。即只包含唯一对象的数据结构。
建议正常使用属性,如myset["key"] = true
。但是,我需要键是对象。我读到Javascript将属性名称转换为字符串,所以我想我不能使用myset[myobject] = true
。
我可以使用数组,但我需要比0 (n)性能更好的东西来添加、查找和删除项。
它需要能够仅通过引用来区分对象,因此给定:
var a = {};
var b = {};
那么a
和b
应该都能加入,因为它们是分开的物体。
基本上,我之后的东西像c++的std::set
,可以存储Javascript对象。什么好主意吗?
ES6提供原生Set
:
let s = new Set();
let a = {};
let b = {};
s.add(a);
console.log(s.has(a)); // true
console.log(s.has(b)); // false
这是一个疯狂的建议…在JSON.stringify(object)
的结果
我在回答我自己的问题,但我想到了一个我认为有趣的替代解决方案,并认为它将是有用的分享。
狼的回答给了我一个主意。提供对象的toString()
方法唯一标识实例,对象的属性可用于存储一组对象。实际上,要存储对象x
,可以使用items[x.toString()] = x;
。请注意,该值就是对象本身,因此可以通过查看所有item
的属性并将所有值转储到数组中来提取对象集。
这是类,我称之为ObjectSet
,完整的。它要求对象由它们的toString()
方法唯一标识,这对于我的目的来说是可以的。add
, remove
和contains
都应该运行在比O(n)时间更好的地方-无论javascript的属性访问效率是多少,希望是O(1)或O(n log n)。
// Set of objects. Requires a .toString() overload to distinguish objects.
var ObjectSet = function ()
{
this.items = {};
this.item_count = 0;
};
ObjectSet.prototype.contains = function (x)
{
return this.items.hasOwnProperty(x.toString());
};
ObjectSet.prototype.add = function (x)
{
if (!this.contains(x))
{
this.items[x.toString()] = x;
this.item_count++;
}
return this;
};
ObjectSet.prototype.remove = function (x)
{
if (this.contains(x))
{
delete this.items[x.toString()];
this.item_count--;
}
return this;
};
ObjectSet.prototype.clear = function ()
{
this.items = {};
this.item_count = 0;
return this;
};
ObjectSet.prototype.isEmpty = function ()
{
return this.item_count === 0;
};
ObjectSet.prototype.count = function ()
{
return this.item_count;
};
ObjectSet.prototype.values = function ()
{
var i, ret = [];
for (i in this.items)
{
if (this.items.hasOwnProperty(i))
ret.push(this.items[i]);
}
return ret;
};
这是不可能的所有对象,但如果你的对象有.toString()
方法实现,它是:
var x = {toString: function(){ return 'foo'; }};
var y = {toString: function(){ return 'bar'; }};
var obj = {};
obj[x] = 'X';
obj[y] = 'Y';
console.log(obj);
// { foo: 'X', bar: 'Y' }
如果你想让这更容易,把它变成一个类:
function myObj(name){
this.name = name;
}
myObj.prototype.toString = function(){ return this.name; }
var obj = {};
obj[new myObj('foo')] = 'X';
obj[new myObj('bar')] = 'Y';
我使用Map,解决了我的案子
const objectsMap = new Map();
const placesName = [
{ place: "here", name: "stuff" },
{ place: "there", name: "morestuff" },
{ place: "there", name: "morestuff" },
];
placesName.forEach((object) => {
objectsMap.set(object.place, object);
});
console.log(objectsMap);
对于您正在尝试做的(对象集),没有原生Javascript实现。您必须自己实现它。一种方法是为对象实现散列函数。该集合的支持数据类型将是一个关联数组,其中数组的键是调用对象的哈希函数获得的值,数组的值是对象本身。
当然,这并不能解决你强调的问题,所以你也需要考虑到平等(也许实现一个equals函数)?
不需要让哈希函数成为对象本身的属性,你可以有一个独立的哈希函数,它接受一个对象作为输入,并生成一个哈希值(大概是通过迭代它的属性)。
使用此方法,您应该能够获得O(1)
进行插入、搜索和删除(不计算散列函数的顺序,这应该不会比O(n)
差,特别是如果您正在迭代其属性以创建散列值)。
ECMAScript6 Set
应该是这样的:
- 标准:http://www.ecma-international.org/ecma-262/6.0/sec-set-o-p-v-throw
- 非官方ES6备忘单:https://github.com/lukehoban/es6features#map--set--weakmap--weakset
Firefox 32上的工作示例(但未在Chromium 37中实现):
if (Set) {
var s = new Set()
var a = {}
var b = {}
var c = {}
s.add(a)
s.add(b)
s.add(b)
assert(s.size === 2)
assert(s.has(a))
assert(s.has(b))
assert(!s.has(c))
}
这并不奇怪,因为{} != {}
: equal默认比较对象地址。
为不支持的浏览器实现的模块:https://github.com/medikoo/es6-set
Javascript Set's不做深度对象比较。
使用lodash,这是一个具有深度对象比较的唯一数组:
const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
_.uniqWith(objects, _.isEqual);
给定一个以下类型的数组:
Array<{ foo: T1, bar: T2 }>
你可以建立一个类型为:
的对应字典{ [foo: T1]: Set<T2> }
查找{ foo: fooValue, bar: barValue }
可以按如下方式执行:
if (fooValue in dictionary && dictionary[fooValue].has(barValue))
这样我们就可以构建一个ObjectSet<T1, T2>
.如果现在有三个元素,可以构建以下字典:
{ [foo: T1]: ObjectSet<T2, T3> }
并通过归纳将ObjectSet扩展为任意数量的属性。
假设您的类型可以用作索引签名。
刚刚输入了这个,只是简单地测试一下:
var Set = function Set()
{
var list = [];
var contains;
this.contains = contains = function(x) {
return list.indexOf(x) >= 0;
}
var put;
this.put = put = function(x) {
if (!contains(x))
list.push(x);
return this;
}
var remove;
this.remove = remove = function(x)
{
var idx = list.indexOf(x);
if (idx >= 0)
list.splice(idx,1);
return this;
}
var all;
this.all = all = function()
{
return list.concat();
}
return this;
}
似乎当以这个为前缀时,函数的内部调用可以工作。例:
var put;
this.put = put = function(x) {
if (!this.contains(x))
list.push(x);
return this;
}
不存在像std::set这样的原生ecmascript类,您可以在其中传递比较器函数。当然,您可以像在任何其他编程语言中一样实现此数据结构。但对于大多数用例,我建议使用更简单的方法:
- 如果你的对象有某种键或id字段,并且你想要O(1)操作,如查找/添加/更新/删除,你应该使用使用这个id字段的映射。
- 如果你没有键,只是想避免集合中的重复,你可以使用map甚至set,使用
JSON.stringify(o, Object.keys(o).sort())
作为id。如果你有嵌套的对象或数组,你也应该缩短它的项目(我没有写那部分)。
我写了一个类,可以匹配:
class ObjectSet {
constructor(iterable) {
this.map = new Map()
if(!iterable) {
return;
}
for(o of iterable) {
this.map.set(this._getId(o), o);
}
}
_getId(o) {
return o.id ? o.id :
JSON.stringify(o, Object.keys(o).sort());
}
get(id) {
return this.map.get(id);
}
has(o) {
return this.map.has(this._getId(o));
}
//add or update. Call "add" if you want to match ES6 Set interface
set(o) {
this.map.set(this._getId(o), o);
}
delete(o) {
this.map.delete(this._getId(o));
}
values() {
return this.map.values();
}
[Symbol.iterator] () {
return this.values();
}
get size() {
return this.map.size;
}
}
//first use case example, fast find and update:
let objSet = new ObjectSet();
//add
objSet.set({
id: 1,
name: "John",
age: 30
});
const eric = {
id: 2,
name: "Eric",
age: 27
}
//add
objSet.set(eric)
console.log(objSet.size); //2
//update object of id 2
objSet.set({
id: 2,
name: "Eric",
age: 28
})
console.log(objSet.size); // still 2
for(o of objSet) {
console.log(o);
}
/*
prints:
{ id: 1, name: 'John Abbot', age: 30 }
{ id: 2, name: 'Eric', age: 28 }
*/
objSet.delete({id: 1});//deletes john
objSet.delete(eric);
console.log(objSet.size);//0
//second use case, lets remove duplicated objects using this class
let points = [
{
x: 10,
y: 40
},
{
x: 10,
y: 40
},
{
x: 10,
y: 35
}
]
//lets remove duplicated objects using ObjectSet:
objSet = new ObjectSet(points);
console.log(o.size);//2
for(o of objSet) {
console.log(o);
}
//same with set method
objSet.set({
x: 10,
y: 35
});
console.log(objSet.size);//still 2
请使用此代码作为参考。
const fruits = [
{name: 'apple', price: 100},
{name: 'apple', price: 100},
{name: 'orange', price: 200},
{name: 'grapes', price: 300}
];
const hasFruitDuplicated = () => {
const duplicatedDeleteFruits = fruits.filter((fruit, index) =>
fruits.findIndex(item => item.name === fruit.name && item.price === fruit.price) === index
);
return duplicatedDeleteFruits;
};
- 通过推送json构建一个数组对象
- 处理对象数组并将它们以 4 个为一组进行分配
- 使一组对象与 jQuery 一起拖动
- 将json的每个对象的一组值推送到不同的数组中
- 旋转一组对象,同时保持其方向不变
- 如何使用用户定义的相等函数在Javascript中创建一组用户定义的对象
- 在JavaScript中,从一组数组递归地构建一个字典/嵌套对象
- LoDash:从一组对象属性中获取一组值
- 在 Javascript 中使用 ForEach 填充数组对象时,我是否需要定义一个数组对象
- 将数组/对象键值替换为另一个数组/对象 javascript 中的键值
- 从一组数据或数组创建对象
- 打字稿函数接受一个或一组对象
- 在 Knockout.js 订阅的帮助下对一组对象进行动画处理
- 如何遍历一组值对 JSON 对象
- 将一个对象制作成一组对象
- Ajax从Laravel接收一组对象
- 我可以在JSON对象中放入一组对象吗?
- js:加载与该对象相关的一组对象
- 在javascript中遍历一组对象,并从该函数返回true
- javascript中的一组对象