通常,问题如下: br />如果我将这些元素按
lastname
和age
分组,则会得到以下结果:var list = [
{name: "1", lastname: "foo1", age: "16"},
{name: "2", lastname: "foo", age: "13"},
{name: "3", lastname: "foo1", age: "11"},
{name: "4", lastname: "foo", age: "11"},
{name: "5", lastname: "foo1", age: "16"},
{name: "6", lastname: "foo", age: "16"},
{name: "7", lastname: "foo1", age: "13"},
{name: "8", lastname: "foo1", age: "16"},
{name: "9", lastname: "foo", age: "13"},
{name: "0", lastname: "foo", age: "16"}
];
经过一番试验,我得出以下解决方案: >
var result = [
[
{name: "1", lastname: "foo1", age: "16"},
{name: "5", lastname: "foo1", age: "16"},
{name: "8", lastname: "foo1", age: "16"}
],
[
{name: "2", lastname: "foo", age: "13"},
{name: "9", lastname: "foo", age: "13"}
],
[
{name: "3", lastname: "foo1", age: "11"}
],
[
{name: "4", lastname: "foo", age: "11"}
],
[
{name: "6", lastname: "foo", age: "16"},
{name: "0", lastname: "foo", age: "16"}
],
[
{name: "7", lastname: "foo1", age: "13"}
]
];
此解决方案有效,但这是正确的最佳方法吗?在我看来还是有点难看。
#1 楼
我不得不写信,您可能应该将forEach和map与Alexey Lebedev的答案结合起来。function groupBy( array , f )
{
var groups = {};
array.forEach( function( o )
{
var group = JSON.stringify( f(o) );
groups[group] = groups[group] || [];
groups[group].push( o );
});
return Object.keys(groups).map( function( group )
{
return groups[group];
})
}
var result = groupBy(list, function(item)
{
return [item.lastname, item.age];
});
#2 楼
函数的主要问题是在最坏的情况下二次时间复杂度。另外,如果我们首先实现通用的groupBy
函数,则按属性分组将变得微不足道。 t禁止扩展对象原型。评论
\ $ \ begingroup \ $
也许您应该考虑将序列化函数传递给groupBy方法。对于toString,存在几个问题:item1 = {lastName:[1,2],age:3}和item2 = {lastName:1,age:[2,3]} and item3 = {lastName:'1,2', age:3}将全部放在您的示例的同一组中。
\ $ \ endgroup \ $
– Tibos
2013年12月11日10:40
\ $ \ begingroup \ $
Tibos:是的,那是一个等待发生的错误。我已经将toString()更改为JSON.stringify()。假设JSON是本地实现的,则速度是可比的。
\ $ \ endgroup \ $
–阿列克谢·列别杰夫(Alexey Lebedev)
2013年12月11日12:16
\ $ \ begingroup \ $
使用stringify作为键非常出色,+ 1
\ $ \ endgroup \ $
– konijn
2013年12月11日13:38
\ $ \ begingroup \ $
@AlexeyLebedev提出了出色的解决方案!假设我要小写所以忽略大小写怎么办?
\ $ \ endgroup \ $
– loretoparisi
19年2月27日在19:39
\ $ \ begingroup \ $
@loretoparisi return [item.lastname.toLowerCase(),item.age];
\ $ \ endgroup \ $
–阿列克谢·列别杰夫(Alexey Lebedev)
19年2月27日在20:01
#3 楼
我发现JavaScript的功能方面是一个很大的优势。当涉及到循环时,Array.prototype.forEach
和表兄弟可以帮助您的代码更具描述性:对象(在这种情况下为Array.prototype
),我留了解决方案的那部分。但是,我将groupByProperties
添加为Array.prototype
的不可枚举属性,因此它不会出现在for..in
枚举中。评论
\ $ \ begingroup \ $
太好了!但是一些测试表明,forEach方法比具有预定义长度的for循环要慢得多。你不同意吗? =)
\ $ \ endgroup \ $
–赛克
2013年12月10日13:03
\ $ \ begingroup \ $
的确,forEach可能比for循环慢大约5倍。它的运行速度仍然相当快,我热衷于清晰地编写代码,并在需要时优化瓶颈。
\ $ \ endgroup \ $
– Tibos
2013年12月10日13:38
\ $ \ begingroup \ $
同样的问题是创建一个局部变量来存储您要遍历的数组的长度。访问局部变量的速度可能比array.length快得多,但是当您进行迭代时,这并不是瓶颈,因此具有已保存变量的循环与具有array.length的常规循环一样快。 (该规则的例外是,当length不是静态属性,而是像实时NodeList一样动态计算时)。
\ $ \ endgroup \ $
– Tibos
2013年12月10日14:10
#4 楼
另一种方法是使用_lodash.groupBy或_lodash.keyBy:您只需编写几行代码即可获得相同的结果:
const Results = _.groupBy(list, 'lastname')
这将按姓氏对结果进行分组。但是,根据您的情况,您需要按多个属性进行分组-您可以使用此代码段来对该函数进行附魔。
当然您可以多次使用此代码。
Lodash允许您安装其代码一对一的模块(npm i lodash.groupby);
我相信这样您将获得更短,更易维护的代码,并且功能清晰。我想这是另一种选择。
评论
\ $ \ begingroup \ $
欢迎使用代码审查!您提出了替代解决方案,但尚未检查代码。请说明您的推理(您的解决方案如何工作以及如何对原始解决方案进行改进),以便作者可以从您的思考过程中学习。
\ $ \ endgroup \ $
– Pimgd
16 Mar 3 '16 at 10:04
\ $ \ begingroup \ $
可能不是代码解决方案,但是除了“简单的代码”(它什么也没有解释)之外,您现在对为什么使用这些功能没有任何解释。
\ $ \ endgroup \ $
– Pimgd
16 Mar 3 '16 at 10:05
评论
有一个名为group-array的npm模块可以满足相同的要求。为什么不仅仅根据这些值对数组排序?