我试图通过共享的类似对象对一些JavasScript对象进行分组。我可以在Ruby中毫不费力地做到这一点,但是对于我的生活(有点令人尴尬),不能在线性时间内在JS中解决这个问题。 JS似乎不允许将对象文字作为键,至少为了减少目的。
由于GraphQL查询,我的数据形状如下:
[
{
id: 1,
name: 'Bob',
room: {
id: 5,
name: 'Kitchen'
}
},
{
id: 3,
name: 'Sheila',
room: {
id: 5,
name: 'Kitchen'
}
},
{
id: 2,
name: 'Tom',
room: {
id: 3,
name: 'Bathroom'
}
}
]
在用户界面中,我们会按照他们所在的房间显示对象。我们需要保留对房间本身的引用,否则我们只需按房间属性排序
我尝试做的是将数据重塑为以下内容:
{
{id: 5, name: 'Kitchen'}: [{id: 1, name: 'Bob'}, {id: 3, name: 'Sheila'}],
{id: 3, name: 'Bathroom'}: [{id: 2, name: 'Tom'}]
}
正如您所看到的,人们被他们所在的房间聚集在一起。
它也可以像这样塑造......
[
{ room: {id: 5, name: 'Kitchen'}, people: [{id: 1, name: 'Bob', ...}] },
{ room: {id: 3, name: 'Bathroom', people: [{id: 2, name: 'Tom'}]
]
然而,它出来了,我们只需要在线性时间内按房间分组的人。
我已使用groupBy
和map
尝试了lodash的reduce
,只是执行将列表放在一起的for
循环等。我不知道因为不能使用对象文字(房间)作为哈希索引,我不知道如何通过内部对象有效地对外部对象进行分组。
非常感谢任何帮助。
更新:增加了关于尝试使用线性时间复杂度的清晰度 - 这个Ruby代码最有效的等价物:
h = Hash.new { |h, k| h[k] = [] }
value.each_with_object(h) { |v, m| m[v[:room]] << v }
答案 0 :(得分:1)
您可以使用lodash#groupBy
和lodash#map
来解决此问题,以收集和转换每个群组。此外,我们使用lodash#omit
从room
数组中删除每个person
的{{1}}对象。
people
var result = _(data)
.groupBy('room.id')
.map(people => ({
room: { ...people[0].room },
people: _.map(people, person => _.omit(person, 'room'))
})).value();
&#13;
var data = [
{
id: 1,
name: 'Bob',
room: {
id: 5,
name: 'Kitchen'
}
},
{
id: 3,
name: 'Sheila',
room: {
id: 5,
name: 'Kitchen'
}
},
{
id: 2,
name: 'Tom',
room: {
id: 3,
name: 'Bathroom'
}
}
];
var result = _(data)
.groupBy('room.id')
.map(people => ({
// make sure to create a new room object reference
// to avoid mutability
room: { ...people[0].room },
people: _.map(people, person => _.omit(person, 'room'))
})).value();
console.log(result);
&#13;
答案 1 :(得分:0)
您可以使用reduce
创建由房间编入索引的人的对象,然后获取该对象的values
,不需要库:
const input=[{id:1,name:'Bob',room:{id:5,name:'Kitchen'}},{id:3,name:'Sheila',room:{id:5,name:'Kitchen'}},{id:2,name:'Tom',room:{id:3,name:'Bathroom'}}]
const output = Object.values(
input.reduce((a, { id, name, room }) => {
const roomName = room.name;
if (!a[roomName]) a[roomName] = { room, people: [] };
a[roomName].people.push({ id, name });
return a;
}, {})
);
console.log(output);
像
这样的对象{id: 5, name: 'Kitchen'}: [{id: 1, name: 'Bob'}, {id: 3, name: 'Sheila'}],
除非结构是Map
,否则你问题中的不能是这样的属性。普通的Javascript对象只能有字符串(/ number)属性。
答案 2 :(得分:0)
另一种方法是使用reduce
来分组房间。
const input = [{
id: 1,
name: 'Bob',
room: {
id: 5,
name: 'Kitchen'
}
},
{
id: 3,
name: 'Sheila',
room: {
id: 5,
name: 'Kitchen'
}
},
{
id: 2,
name: 'Tom',
room: {
id: 3,
name: 'Bathroom'
}
}
];
const res = input
.map(person => ({
person: {
id: person.id,
name: person.name
},
room: person.room
}))
.reduce((rooms, person) => {
const room = rooms.find(room => room.id === person.room.id) ||
{ room: person.room };
const idx = rooms.indexOf(room);
room.people = room.people ?
[...room.people, person.person] :
[person.person];
return Object.assign(rooms, {
[idx === -1 ? rooms.length : idx]: room
});
}, []);
console.log(res);