按属性排序数组项并保持原始数组

时间:2017-02-12 11:39:21

标签: javascript arrays sorting grouping

我编写了这个函数,用一个或多个属性对数组进行分组:

var groupBy = function (fields, data) {
    var groups = {};
    for (var i = 0; i < data.length; i++) {
        var item = data[i];
        var container = groups;
        for (var j = 0; j < fields.length; j++) {
            var groupField = fields[j];
            var groupName = item[groupField];
            if (!container[groupName]) {
                container[groupName] = j < fields.length - 1 ? {} : [];
            }
            container = container[groupName];
        }
        container.push(item);
    }
    return groups;
};

例如,如果我使用此输入

var animals = [
    {type: "cat", name: "Max"},
    {type: "dog", name: "Charlie"},
    {type: "cat", name: "Zoe"},
    {type: "dog", name: "Abby"},
    {type: "cat", name: "Molly"}
];

var groupedAnimals = groupBy(["type"], animals);

我得到了这个输出:

{
    "cat": [
        {
            "type": "cat",
            "name": "Max"
        },
        {
            "type": "cat",
            "name": "Zoe"
        },
        {
            "type": "cat",
            "name": "Molly"
        }
    ],
    "dog": [
        {
            "type": "dog",
            "name": "Charlie"
        },
        {
            "type": "dog",
            "name": "Abby"
        }
    ]
}

到目前为止一切都还可以......问题是我需要键来反映原始输入数组的顺序。因此,如果第一项是猫,我迭代组键,我需要第一个键是猫。由于JS中的对象明确没有排序,我无法保证正确的顺序。我怎样才能做到这一点?

编辑:

我想结果必须是这样的:

groupBy(["type", "name"], animals)

应该产生:

[
  {
    "group": "cat",
    "items": [
      {
        "group": "max",
        "items": [
          {
            "type": "cat",
            "name": "Max"
          }
        ]
      },
      {
        "group": "Zoe",
        "items": [
          {
            "type": "cat",
            "name": "Zoe"
          }
        ]
      },
      {
        "group": "Molly",
        "items": [
          {
            "type": "cat",
            "name": "Molly"
          }
        ]
      }
    ]
  },
  {
    "group": "dog",
    "items": [
      {
        "group": "Charlie",
        "items": [
          {
            "type": "dog",
            "name": "Charlie"
          }
        ]
      },
      {
        "group": "Abby",
        "items": [
          {
            "type": "dog",
            "name": "Abby"
          }
        ]
      }
    ]
  }
]

3 个答案:

答案 0 :(得分:1)

您可以将结果数据结构更改为数组数组,以便保持顺序。

&#13;
&#13;
var animals = [
 {type: "cat", name: "Max"},
 {type: "dog", name: "Charlie"},
 {type: "cat", name: "Zoe"},
 {type: "dog", name: "Abby"},
 {type: "cat", name: "Molly"}
];

var result = []
animals.forEach(function(e) {
  if(!this[e.type]) {
    this[e.type] = [e.type, []]
    result.push(this[e.type])
  }
  this[e.type][1].push(e)
}, {})

console.log(result)
&#13;
&#13;
&#13;

要按多个字段分组,您可以将数组作为第一个参数传递给函数

&#13;
&#13;
var animals = [
 {type: "cat", name: "Max", i: 2},
 {type: "dog", name: "Charlie", i: 2},
 {type: "cat", name: "Zoe", i: 2},
 {type: "dog", name: "Abby", i: 1},
 {type: "cat", name: "Molly", i: 2}
];

function groupBy(fields, data) {
  var result = []
  data.forEach(function(e) {
    var group = fields.map(a => e[a]).join('-')

    if (!this[group]) {
      this[group] = [group, []]
      result.push(this[group])
    }
    this[group][1].push(e)
  }, {})
  return result
}

console.log(groupBy(['type', 'i'], animals))
&#13;
&#13;
&#13;

答案 1 :(得分:1)

您可以稍微更改一下您的功能,以便在每个组级别添加一个特殊属性,按照应该迭代的顺序列出键:

&#13;
&#13;
var groupBy = function (fields, data) {
    var groups = { _keys: [] };
    //             ^^^^^^^^^
    for (var i = 0; i < data.length; i++) {
        var item = data[i];
        var container = groups;
        for (var j = 0; j < fields.length; j++) {
            var groupField = fields[j];
            var groupName = item[groupField];
            if (!container[groupName]) {
                container[groupName] = j < fields.length - 1 ? { _keys: [] } : [];
    //                                                           ^^^^^^^^^
                container._keys.push(groupName);
    //          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            }
            container = container[groupName];
        }
        container.push(item);
    }
    return groups;
};

var animals = [
    {type: "cat", name: "Max"},
    {type: "dog", name: "Charlie"},
    {type: "cat", name: "Zoe"},
    {type: "dog", name: "Abby"},
    {type: "cat", name: "Molly"}
];

var groupedAnimals = groupBy(["type"], animals);

console.log(groupedAnimals);

// Output types in order:
console.log('output types in fixed order:');
groupedAnimals._keys.forEach(function (key, i) {
    console.log(i, key, groupedAnimals[key]);
});
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

答案 2 :(得分:1)

您可以使用完全动态的功能对各种键和深度进行分组。

此提案适用于每个级别的哈希表和数组。

&#13;
&#13;
function groupBy(keys, array) {
    var result = [];
    array.forEach(function (a) {
        keys.reduce(function (r, k) {
            if (!r[a[k]]) {
                r[a[k]] = { _: [] };
                r._.push({ group: a[k], items: r[a[k]]._ });
            }
            return r[a[k]];
        }, this)._.push(a);
    }, { _: result });
    return result;
}

var animals = [{ type: "cat", name: "Max" }, { type: "dog", name: "Charlie" }, { type: "cat", name: "Zoe" }, { type: "dog", name: "Abby" }, { type: "cat", name: "Molly" }];

console.log(groupBy(["type", "name"], animals));
console.log(groupBy(["type"], animals));
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;