排序对象数组,然后按ID分组(JavaScript)

时间:2018-05-21 18:28:40

标签: javascript sorting

我有一系列对象需要一些非传统的排序。每个对象都包含 id 字符串和 num int。一些未分类的虚拟数据:

[{"id":"ABC","num":111},
{"id":"DEF","num":130},
{"id":"XYZ","num":115},
{"id":"QRS","num":98},
{"id":"DEF","num":119},
{"id":"ABC","num":137},
{"id":"LMN","num":122},
{"id":"ABC","num":108}]

我需要按 num 对提升进行排序 - 但是,如果 ID 出现多次,那么 id 的其他记录应该“浮动”在“与其下一个最小的 num 一起居住在其兄弟之下的位置。

最终结果将是:

[{"id":"QRS","num":98},
{"id":"ABC","num":108},
{"id":"ABC","num":111},
{"id":"ABC","num":137},
{"id":"XYZ","num":115},
{"id":"DEF","num":119},
{"id":"DEF","num":130},
{"id":"LMN","num":122}]

实际数组可能包含15k +记录,因此非常感谢任何有效的解决方案。带有一些嵌套的“ifs”的.sort(function(a,b) {...})可以很好地进行基本排序,但是我对“浮动”逻辑感到困惑。提前谢谢。

编辑:到目前为止(基本嵌套排序):

const sortedData = origData.sort(function(a, b) {
  if (a.num === b.num) {
    if (a.id === b.id) {
      return a.id.localeCompare(b.id);
    }
  }
  return a.num - b.num;
});

4 个答案:

答案 0 :(得分:0)

这就是我想出的。您需要首先按ID分组并将分组的ID存储到数组中。然后,按num asc排序,并考虑任何分组的ID:

编辑:修复了分组ID

asc排序

var data = [{"id":"ABC","num":111},
{"id":"DEF","num":130},
{"id":"XYZ","num":115},
{"id":"QRS","num":98},
{"id":"DEF","num":119},
{"id":"ABC","num":137},
{"id":"LMN","num":122},
{"id":"ABC","num":108}];

const sortArray = arr => {
  let matchingIds = [];
  const sorted = arr.sort( (a,b) => {
    if(a.id === b.id){
      matchingIds.push(a.id);
      return 0;
    }else{
      return 1;
    }
  }).sort( (a,b) => {
  
    if(matchingIds.indexOf(a.id) > -1 && matchingIds.indexOf(b.id) > -1 && a.id === b.id) {
      return a.num - b.num;
    }
    if(matchingIds.indexOf(a.id) > -1 || matchingIds.indexOf(b.id) > -1) {
      return 0;
    }
    return a.num - b.num;
  });
  
  console.log(sorted);
}

sortArray(data);

答案 1 :(得分:0)

一种方法是

  • 第一组由id
  • 然后按num
  • 对每个组进行排序
  • 然后按min(num)
  • 对组进行排序
  • 然后联合小组

let data = [{"id":"ABC","num":111},
{"id":"DEF","num":130},
{"id":"XYZ","num":115},
{"id":"QRS","num":98},
{"id":"DEF","num":119},
{"id":"ABC","num":137},
{"id":"LMN","num":122},
{"id":"ABC","num":108}];

const groupById = (acc, item) => {
  const id = item.id;
  if(id in acc){
    acc[id].push(item);
  }else{
    acc[id] = [item];
  }
  return acc;
};
const sortByNum = (a,b) => a.num - b.num;
const sortByMinNum = (a,b) => a[0].num - b[0].num;

const groups = Object.values(data.reduce(groupById, {}))
  .map(group => group.sort(sortByNum))
  .sort(sortByMinNum);
  
console.log([].concat(...groups));
.as-console-wrapper{top:0;max-height:100%!important}

另一种方法是

  • 首先通过id确定最小数量
  • 然后按minNum和num
  • 排序

let data = [{"id":"ABC","num":111},
{"id":"DEF","num":130},
{"id":"XYZ","num":115},
{"id":"QRS","num":98},
{"id":"DEF","num":119},
{"id":"ABC","num":137},
{"id":"LMN","num":122},
{"id":"ABC","num":108}];


const minNumById = data.reduce((acc, item) => {
  const id = item.id;
  if(id in acc){
    acc[id] = Math.min(acc[id], item.num);
  }else{
    acc[id] = item.num;
  }
  return acc;
}, {});

data.sort((a, b) => minNumById[a.id] - minNumById[b.id] || a.num - b.num);


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

答案 2 :(得分:0)

  • 我首先创建了一张地图。

  • 此地图基本上会以async作为关键字及其所有值 阵列。

  • 为地图的每个键对单个数组进行排序。

  • 现在,在新的对象集合中收集所有这些,对它们进行排序 再次比较id元素。

  • 现在,只需循环遍历新集合并将其推送到结果中 阵列



only first




答案 3 :(得分:0)

我是函数式编程库Ramda的忠实粉丝。 (免责声明:我是其作者之一。)我倾向于考虑简单,可重复使用的功能。

当我想到如何解决这个问题时,我会通过Ramda的观点来思考它。我可能会像这样解决这个问题:



const {pipe, groupBy, prop, map, sortBy, values, head, unnest} = R;

const transform = pipe(
  groupBy(prop('id')),
  map(sortBy(prop('num'))),
  values,
  sortBy(pipe(head, prop('num'))),
  unnest
)

const data = [{"id": "ABC", "num": 111}, {"id": "DEF", "num": 130}, {"id": "XYZ", "num": 115}, {"id": "QRS", "num": 98}, {"id": "DEF", "num": 119}, {"id": "ABC", "num": 137}, {"id": "LMN", "num": 122}, {"id": "ABC", "num": 108}]

console.log(transform(data))

<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
&#13;
&#13;
&#13;

我认为这是相当可读的,至少一旦你理解了管道创建了一个函数管道,每个函数都将其结果交给下一个。

现在,通常没有理由包含像Ramda这样的大型库来解决一个相当简单的问题。但该版本中使用的所有功能都可以轻松重用。因此,尝试创建这些函数的自己版本并将其保留给应用程序的其余部分可能是有意义的。事实上,像Ramda这样的图书馆实际上是如何构建的。

所以这里有一个版本,它具有这些函数的简单实现,可以放在实用程序库中:

&#13;
&#13;
const groupBy = (fn) => (arr) => arr.reduce((acc, val) => (((acc[fn(val)] || (acc[fn(val)] = [])).push(val)), acc), {})
const head = (arr) => arr[0]
const mapObj = (fn) => (obj) => Object.keys(obj).reduce((acc, val) => (acc[val] = fn(obj[val]), acc), {})
const pipe = (...fns) => (arg) => fns.reduce((a, f) => f(a), arg)
const prop = (name) => (obj) => obj[name]
const values = Object.values
const unnest = (arr) => [].concat(...arr)
const sortBy = (fn) => (arr) => arr.slice(0).sort((a, b) => {
  const aa = fn(a), bb = fn(b)
  return aa < bb ? -1 : aa > bb ? 1 : 0
})

const transform = pipe(
  groupBy(prop('id')),
  mapObj(sortBy(prop('num'))),
  values,
  sortBy(pipe(head, prop('num'))),
  unnest
)

const data = [{"id": "ABC", "num": 111}, {"id": "DEF", "num": 130}, {"id": "XYZ", "num": 115}, {"id": "QRS", "num": 98}, {"id": "DEF", "num": 119}, {"id": "ABC", "num": 137}, {"id": "LMN", "num": 122}, {"id": "ABC", "num": 108}]

console.log(transform(data))
&#13;
&#13;
&#13;