【问题标题】:Sort and group an array of objects by two keys and create array of grouped items with new keys通过两个键对对象数组进行排序和分组,并使用新键创建分组项数组
【发布时间】:2020-01-28 09:01:00
【问题描述】:

我有以下数组:

const items = [
  { name: 'john', class: 'one', section: 'a' },
  { name: 'john2', class: 'one', section: 'b' },
  { name: 'john3', class: 'one', section: 'a' },
  { name: 'john4', class: 'two', section: 'a' },
  { name: 'john5', class: 'two', section: 'b' },
  { name: 'john6', class: 'two', section: 'b' },
  { name: 'john7', class: 'three', section: 'a' },
  { name: 'john8', class: 'four', section: 'a' },
  { name: 'john9', class: 'four', section: 'b' }
];

我希望它以这种方式分组:

[
    {
        'oneA': [
            { name: 'john', class: 'one', section: 'a' },
            { name: 'john3', class: 'one', section: 'a' }
        ]
    },
    {
        'oneB': [
            { name: 'john2', class: 'one', section: 'b' }
        ]
    },
    {
        'twoA': [
            { name: 'john4', class: 'two', section: 'a' }
        ]
    },
    {
        'twoB': [
            { name: 'john5', class: 'two', section: 'b' },
            { name: 'john6', class: 'two', section: 'b' }
        ]
    },
    {
        'threeA': [
            { name: 'john7', class: 'three', section: 'a' }
        ]
    },
    {
        'fourA': [
            { name: 'john8', class: 'four', section: 'a' }
        ]
    },
    {
        'fourB': [
            { name: 'john9', class: 'four', section: 'b' }
        ]
    }
]

我尝试过这样的事情:

items.sort(function (a, b) {
  if (a.class > b.class) return 1;
  if (a.class < b.class) return -1;

  if (a.section > b.section) return 1;
  if (a.section < b.section) return -1;
})

这可以按我的意愿对数组进行排序,但它没有像我上面描述的那样分组。 你知道达到那个目的的方法吗?

【问题讨论】:

  • 为什么你的结果需要包含一个对象数组,每个对象都有一个键?为什么不只是一个具有多个键的对象?排序逻辑是什么?您是否尝试根据数字的文本表示进行排序?
  • @RobbyCornelissen 我需要一个对象数组,因为我会在它上面循环,然后我会在每个数组中循环。
  • 你确定下面的节点也有“三”类值吗? ``` { 'fourA': [ { name: 'john7', class: 'three', section: 'a' }, { name: 'john8', class: 'four', section: 'a' } ] } ```

标签: javascript arrays sorting object grouping


【解决方案1】:

您可以通过 Map 将它们分组。

它生成一个class 和大写section 的键,尝试从映射中获取值或获取一个空数组以使用实际对象传播一个新数组。然后它将这个数组设置为地图中的新值。

Array.from 从映射中获取所有键/值对并使用computed property name 构建新对象。

const
    getKey = o => `${o.class}${o.section.toUpperCase()}`,
    items = [{ name: 'john', class: 'one', section: 'a' }, { name: 'john2', class: 'one', section: 'b' }, { name: 'john3', class: 'one', section: 'a' }, { name: 'john4', class: 'two', section: 'a' }, { name: 'john5', class: 'two', section: 'b' }, { name: 'john6', class: 'two', section: 'b' }, { name: 'john7', class: 'three', section: 'a' }, { name: 'john8', class: 'four', section: 'a' }, { name: 'john9', class: 'four', section: 'b' }],
    result = Array.from(
        items.reduce(
            (m, o) => m.set(getKey(o), [...(m.get(getKey(o)) || []), o]),
            new Map
        ),
        ([k, v]) => ({ [k]: v })
    );

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 您能否更详细地解释一下代码,以便更好地理解它的作用?
  • 天哪,我希望你不要这样在生产环境中编码)
  • @NinaScholz 我认为有几件事,例如:你定义 itemsresult 像 global。如果你可以在循环体中连接字符串,你为什么要定义getKey。我不确定,但我认为您可以使用{} 代替new Map,因为它对我们的社区来说更熟悉。我认为你异常地解决了问题,但不是以最好的方式
  • UPDitemsresult 不是全球性的,很抱歉 :)
  • 是的,它有效,但并不像@SaveliTomac 所说的那样熟悉。
【解决方案2】:
const items = [
    { name: 'john', class: 'one', section: 'a' },
    { name: 'john2', class: 'one', section: 'b' },
    { name: 'john3', class: 'one', section: 'a' },
    { name: 'john4', class: 'two', section: 'a' },
    { name: 'john5', class: 'two', section: 'b' },
    { name: 'john6', class: 'two', section: 'b' },
    { name: 'john7', class: 'three', section: 'a' },
    { name: 'john8', class: 'four', section: 'a' },
    { name: 'john9', class: 'four', section: 'b' }
];

let newArray = items.reduce((main, curr) => {
    let index = main.findIndex(obj => Object.keys(obj).includes(curr.class + curr.section))
    if (index == -1) main.push({ [curr.class + curr.section]: [curr] })
    else main[index][curr.class + curr.section].push(curr)
    return main
}, [])
console.log(newArray)
【解决方案3】:

您可以使用reduce 函数,例如:

const items = [
  { name: 'john', class: 'one', section: 'a' },
  { name: 'john2', class: 'one', section: 'b' },
  { name: 'john3', class: 'one', section: 'a' },
  { name: 'john4', class: 'two', section: 'a' },
  { name: 'john5', class: 'two', section: 'b' },
  { name: 'john6', class: 'two', section: 'b' },
  { name: 'john7', class: 'three', section: 'a' },
  { name: 'john8', class: 'four', section: 'a' },
  { name: 'john9', class: 'four', section: 'b' }
];

const groupedItems = items.reduce((result, current) => {
  const key = current.class + current.section.toUpperCase();
  
  if (Array.isArray(result[key])) {
    result[key].push(current);
  } else {
    result[key] = [current];
  }
  
  return result;
}, {});

const expectedResult = Array.from(Object.keys(groupedItems), (elem) => ({[elem]: groupedItems[elem]}));

console.log(expectedResult);

【讨论】:

  • 它有效,但我得到的订单和你的不一样。
  • 你可以对reduce的结果进行排序,很简单,我会更新我的答案
  • 好的,我做到了。无论如何,您的解决方案会生成一个对象,而不是我在问题中描述的对象数组。
  • @smartmouse 你的预期结果是来自一个元素的数组,你可以像这样得到它:[groupedItems]
  • @smartmouse 你确定我的标准输出不是你所期望的吗?请再检查一遍
【解决方案4】:

我建议省略结果的外部数组,因为对我来说,如果你想创建一组对象,那么拥有它是没有意义的。

要解决您的问题,只需遍历所有项目并检查您的结果对象是否已经具有所需的键。如果没有,则创建它,然后将当前项插入到相应的数组中。

const items = [
  { name: 'john', class: 'one', section: 'a' },
  { name: 'john2', class: 'one', section: 'b' },
  { name: 'john3', class: 'one', section: 'a' },
  { name: 'john4', class: 'two', section: 'a' },
  { name: 'john5', class: 'two', section: 'b' },
  { name: 'john6', class: 'two', section: 'b' },
  { name: 'john7', class: 'three', section: 'a' },
  { name: 'john8', class: 'four', section: 'a' },
  { name: 'john9', class: 'four', section: 'b' }
];

let result = {};

items.forEach(item => {
  let key = `${item.class}${item.section.toUpperCase()}`;
  if(!result.hasOwnProperty(key)) {
    result[key] = [];
  }
  result[key].push(item);
});

console.info(result);

【讨论】:

  • 为什么这个答案被否决了?在我看来,与只是粘贴一堆代码的其他答案相比,它为解决方案提供了最好的解释。
【解决方案5】:

如果您希望在不使用内置函数的情况下使用纯 javascript 获得答案以便更好地理解。

var items = [
    { name: 'john', class: 'one', section: 'a' },
    { name: 'john2', class: 'one', section: 'b' },
    { name: 'john3', class: 'one', section: 'a' },
    { name: 'john4', class: 'two', section: 'a' },
    { name: 'john5', class: 'two', section: 'b' },
    { name: 'john6', class: 'two', section: 'b' },
    { name: 'john7', class: 'three', section: 'a' },
    { name: 'john8', class: 'four', section: 'a' },
    { name: 'john9', class: 'four', section: 'b' }
];
var resultArray =[];
for(var i=0;i<items.length;i++){
    if(resultArray[items[i]['class']+items[i]['section'].toUpperCase()]){
        resultArray[items[i]['class']+items[i]['section'].toUpperCase()].push(items[i]);
    } else {
        var a ="";
        a = items[i]['class']+items[i]['section'].toUpperCase();
        resultArray[a]=[];
        resultArray[a].push(items[i])
    }
}

console.log(resultArray);

【讨论】:

  • 它有效,但项目的顺序与我在问题中描述的不同。
  • 订单正常,您能再验证一下吗?我现在已经包含了之前遗漏的部分的大写部分。谢谢
猜你喜欢
  • 2018-02-20
  • 1970-01-01
  • 2022-01-11
  • 2020-04-13
  • 2021-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-04
相关资源
最近更新 更多