将事物分类为类型有什么好的JavaScript模式?

时间:2010-11-21 01:15:57

标签: javascript ruby arrays design-patterns associative-array

我正在寻找一种方法(在JavaScript中)将一组对象收集到多个数组中,其中每个数组包含某种类型的对象,并且数组作为值存储在关联数组中,其中键为类型。例如:

输入:

[<apple>, <cat>, <pear>, <mercedes>, <dog>, <ford>, <orange>]

输出:

{
  'fruit': [<apple>, <pear>, <orange>],
  'animal': [<cat>, <dog>],
  'car': [<mercedes>, <ford>]
}

在ruby中,您可以执行以下操作:

things_by_type = {}
things.each do |thing|
  (things_by_type[thing.type] ||= []) << thing
end

这很简洁。

在JavaScript中做同样的事情,简洁高效的好模式是什么?我可以做这样的事情,但它并不那么好:

var thing, things_by_type = {};
for (var i = 0; i < things.length; i++) {
  thing = things[i];
  if(things_by_type[thing.type]) {
    things_by_type[thing.type].push(thing);
  } else {
    things_by_type[thing.type] = [thing];
  }
}

3 个答案:

答案 0 :(得分:1)

我不确定它是不是一个好的模式,但它与你的红宝石样本类似:

var things_by_type = {};
for (var i in things) {
  var thing = things[i];
  (things_by_type[thing.type] || (things_by_type[thing.type] = [])).push(thing);
}

如果你可以假设Javascript 1.6:

var things_by_type = {};
things.forEach(function(thing) {
  (things_by_type[thing.type] || (things_by_type[thing.type] = [])).push(thing);
})

答案 1 :(得分:1)

  

在ruby中,您可以执行以下操作:

things_by_type = {}
things.each do |thing|
  (things_by_type[thing.type] ||= []) << thing
end
     

这很简洁。

实际上,你可以做得更好。

首先,Hash.new接受一个块参数,每次引用一个不存在的键时都会调用该参数。您可以使用它来创建该密钥。这样你就可以摆脱块内的条件逻辑。

things_by_type = Hash.new {|h, k| h[k] = [] }
things.each do |thing|
  things_by_type[thing.type] << thing
end

其次,你在这里所谓的foldreduce:你将一个集合(对象数组)“折叠”或“缩减”成一个单独的值(哈希,令人困惑的也恰好是一个集合,但仍然是一个单一的价值。)

通常可以通过查找初始化某个变量的位置,然后遍历集合并在循环的每次迭代中操作该变量来轻松发现此模式。

Ruby通过Enumerable#reduce方法内置折叠:

things.reduce(Hash.new {|h, k| h[k] = [] }) do |h, thing|
  h.tap { h[thing.type] << thing }
end

但是你真正做什么,是通过其元素的type属性对数组进行分组,它也以Enumerable#group_by的形式构建在Ruby中:

things.group_by {|thing| thing.type }

使用Symbol#to_proc

可以进一步简化
things.group_by(&:type)

不幸的是,ECMAScript没有groupBy,也没有非现有属性的默认值,但 Array.prototype.reduce

things.reduce(function (acc, thing) {
    (acc[thing.type] || (acc[thing.type] = [thing])).push(thing);
    return acc;
}, {});

答案 2 :(得分:0)

几乎相同的代码,但工作方式有点不同,您可以更轻松地使用花哨的集合函数并将逻辑分开:

var a = {set:function(type,thing){
  if (this[type]) {
    this[type].push(thing);
  } else {
    this[type] = [thing];
  }
}};

a.set('a',0);
a.set('b',1);
a.set('a',2);