Javascript - 对数组进行分组,然后根据长度对分组结果进行排序

Javascript - grouping an array and then sorting the grouped result based on length

这是数组;

var dataArr = [
  {
    "permalink": /* link*/
      "subreddit": "mac"
  }, {
    "permalink": /* link*/
      "subreddit": "worldnews"
  }, {
    "permalink": /* link*/
      "subreddit": "MushroomGrowers"
  }, {
    "permalink": /* link*/
      "subreddit": "chrome"
  }, {
    "permalink": /* link*/
      "subreddit": "onions"
  }, {
    "permalink": /* link*/
      "subreddit": "onions"
  }, {
    "permalink": /* link*/
      "subreddit": "SquaredCircle"
  }.....
]

基于使用下划线的“subreddit”键分组似乎很简单

const grouped = _.groupBy( dataArr, 'subreddit' )

returns 像这样的对象

{
  ArtisanVideos: [
    {
      "permalink": ' link ',
      subreddit: "ArtisanVideos"
    },
    {
      "permalink": ' link ',
      subreddit: "ArtisanVideos"
    },
    {
      "permalink": ' link ',
      subreddit: "ArtisanVideos"
    }
  ],
  chrome: [
    {
      "permalink": ' link ',
      subreddit: "chrome"
    }
  ],
  laravel: [
    {
      "permalink": ' link ',
      subreddit: "laravel"
    },
    {
      "permalink": ' link ',
      subreddit: "laravel"
    },
    {
      "permalink": ' link ',
      subreddit: "laravel"
    }
  ],
  mac: [
    {
      "permalink": ' link ',
      subreddit: "mac"
    }
  ]
}

现在,如何根据数组的长度对分组的对象进行排序

好的。给你。

首先,我们对数据进行排序:

    let sorted = data.sort((a, b) => (a.subreddit > b.subreddit) ? 1 : -1);
    console.log("sorted: ", sorted);

请注意,我们仅按 属性 subreddit 进行排序,以便将它们组合在一起以进行下一步。

接下来,如果 属性 未定义,我们通过创建一个新数组来创建一个新对象:

  let output = {};
    data.forEach((elem) => {
      if(output[elem.subreddit] === undefined){
        output[elem.subreddit] = new Array();
      }
      output[elem.subreddit].push(elem);
    });

最后,我们进行测试以确保一切都如我们所料:

console.log("same? :", JSON.stringify(expected) == JSON.stringify(output));

let data = [
  { "permalink" : "https://www.apple.com", "subreddit": "mac" }, 
  { "permalink" : "https://www.microsoft.com", "subreddit": "microsoft" }, 
  { "permalink" : "https://www.xcode.com", "subreddit": "mac" }, 
  { "permalink" : "https://www.xbox.com", "subreddit": "microsoft" }, 
];

let expected = {
  "mac": [
    { "permalink" : "https://www.apple.com", "subreddit": "mac" }, 
    { "permalink" : "https://www.xcode.com", "subreddit": "mac" }
  ],
  "microsoft": [
    { "permalink" : "https://www.microsoft.com", "subreddit": "microsoft" }, 
    { "permalink" : "https://www.xbox.com", "subreddit": "microsoft" }, 
  ]
};


let sorted = data.sort((a, b) => (a.subreddit > b.subreddit) ? 1 : -1);
console.log("sorted: ", sorted);

let output = {};
data.forEach((elem) => {
  if(output[elem.subreddit] === undefined){
    output[elem.subreddit] = new Array();
  }
  output[elem.subreddit].push(elem);
});

console.log("output: ", output);
console.log("same? :", JSON.stringify(expected) == JSON.stringify(output));

尽情享受吧。

根据我上面的评论...

"In case the OP depends on key insertion order (though it would even work) of an object (because this is what the OP is asking for) then the OP's entire data handle approach is at question. What one could do instead ... create a sorted array of object-items from the former object of grouped entries."

下一个提供的解决方案完全符合已经提出的建议。

为了实现中间分组目标,可以 reduce 将给定的 data-structure 放入分组条目的对象中(每个条目包含一些前 data-array 项目的数组.

每个 entries now can be mapped into an own object of the still unsorted result array which within a last step will be sort 通过每个对象的唯一 array-value length-属性 或通过单个 属性 名称的区域设置比较(键)。

function groupAndCollectBySameKeyValue({ key, result }, item) {
  const groupValue = item[key];
  (result[groupValue] ??= []).push(item);
  return { key, result };
}

var dataArr = [
  { permalink: 'link', subreddit: 'laravel' },
  { permalink: 'link', subreddit: 'mac' },
  { permalink: 'link', subreddit: 'ArtisanVideos' },
  { permalink: 'link', subreddit: 'laravel' },
  { permalink: 'link', subreddit: 'ArtisanVideos' },
  { permalink: 'link', subreddit: 'chrome' },
  { permalink: 'link', subreddit: 'ArtisanVideos' },
  { permalink: 'link', subreddit: 'laravel' },
];
console.log(
  dataArr
    .reduce(groupAndCollectBySameKeyValue, {
      key: 'subreddit',
      result: {},
    })
    .result
);
console.log(
  Object
    .entries(
      dataArr
        .reduce(groupAndCollectBySameKeyValue, {
          key: 'subreddit',
          result: {},
        })
        .result
    )
    // create object from grouped entry (key-value pair).
    .map(([key, value]) => ({ [key]: value }))
    .sort((a, b) =>
      // ... either by array length ...
      Object.values(b)[0].length - Object.values(a)[0].length
      // ... or by locale alphanumeric precedence.
      || Object.keys(a)[0].localeCompare(Object.keys(b)[0])
    )
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

其中一个原因也能达到OP的初衷,但不推荐。人们不应该真正依赖对象的键插入顺序。

function groupAndCollectBySameKeyValue({ key, result }, item) {
  const groupValue = item[key];
  (result[groupValue] ??= []).push(item);
  return { key, result };
}

var dataArr = [
  { permalink: 'link', subreddit: 'laravel' },
  { permalink: 'link', subreddit: 'mac' },
  { permalink: 'link', subreddit: 'ArtisanVideos' },
  { permalink: 'link', subreddit: 'laravel' },
  { permalink: 'link', subreddit: 'ArtisanVideos' },
  { permalink: 'link', subreddit: 'chrome' },
  { permalink: 'link', subreddit: 'ArtisanVideos' },
  { permalink: 'link', subreddit: 'laravel' },
];
console.log(
  dataArr
    .reduce(groupAndCollectBySameKeyValue, {
      key: 'subreddit',
      result: {},
    })
    .result
);
console.log(
  Object
    .entries(
      dataArr
        .reduce(groupAndCollectBySameKeyValue, {
          key: 'subreddit',
          result: {},
        })
        .result
    )
    .sort(([aKey, aValue], [bKey, bValue]) =>
      // array length first, or locale property name comparison.
      bValue.length - aValue.length || aKey.localeCompare(bKey)
    )
    .reduce((result, [key, value]) =>
      // create object by aggregating entries while following
      // the above sorted key precedence / key insertion order.
      Object.assign(result, { [key]: value }), {}
    )
);
.as-console-wrapper { min-height: 100%!important; top: 0; }