如何根据每行的总和对2D数组进行排序?

时间:2017-05-01 16:56:28

标签: javascript sorting

对不起,一个简单的问题:

我有一个2D元素数组,其中每个元素都封装了一个int值。像这样:

MyComponent = {... , value: 2, ...} // each instance has a value property

MyArray = [
            [Component1, Component2],
            [Component3],
            [Component4, Component5, Component6, Component7],
            ... ]

我想根据每行中value个元素的总和对我的2D数组行进行排序。

稍微详细一点:我有多组选择(组合)供用户选择,其中任何组中的每个选项都有一定的值。我想首先显示sumtotal最高的组合(因此它是降序)。

在javascript中实现这一目标的快速有效方法是什么?

3 个答案:

答案 0 :(得分:1)

以下排序函数预先计算行总和以避免冗余求和,并将总和存储在Map中,以便在array.sort()回调中快速检索:

// Sort a 2D array in-place by the sum of its rows:
function sortByRowSum(array) {
  let sums = new Map(array.map(row =>
    [row, row.reduce((sum, element) => sum + element, 0)]
  ));
  return array.sort((a, b) => sums.get(a) - sums.get(b));
}

// Example:
console.log(sortByRowSum([[1, 3], [2], [0, 1, 8], [0]]));

由于您的行元素不是上面示例中的简单数字,因此您需要将reduce回调调整为行元素的结构,例如:

let sums = new Map(array.map(row =>
  [row, row.reduce((sum, component)=> sum + component.value, 0)]
));

答案 1 :(得分:1)



var originalArray = [
    [{value: 2}, {value: 3}],
    [{value: 11}],
    [{value: 1}, {value: 2}, {value: 3}, {value: 4}]
];
var sortedArray = originalArray.sort(function(row1, row2) {
    add = function(a, b) {return a + b.value};
    return row1.reduce(add, 0) - row2.reduce(add, 0);
});
console.log(sortedArray);




让我们打破这个,不管吗?

我们从原始数组开始:

var originalArray = [
    [{value: 2}, {value: 3}],
    [{value: 11}],
    [{value: 1}, {value: 2}, {value: 3}, {value: 4}]
];

然后我们对它进行排序。让我们从内到外发挥作用。首先,我们如何计算一行的总和?我们可以使用reduce函数:



exampleRow = [{value: 2}, {value: 3}];
add = function(a, b) {return a + b.value};
console.log(exampleRow.reduce(add, 0));




reduce函数接受另一个函数(在这种情况下为add)并使用它迭代一个数组并将 reduce 它转换为单个项目(通常是一个数字) )。 reduce需要一个函数,它接受两个参数 - 运行总计和下一个项目。大致减少以下内容:

array.prototype.reduce = function(fn, initial) {
    runningTotal = initial;
    for (var i = 0; i <= array.length; i++) {
        runningTotal = fn(runningTotal, array[i]);
    }
    return runningTotal;
}

总之,它以初始值开头,然后反复运行你的函数,每次运行都使用最后一次运行的输出和数组中的下一项。

在我们的案例中,fn函数为add

add = function(a, b) {return a + b.value};

add非常简单;它需要运行总计(a)并添加下一个项目valueb)。在整个数组中,这简化为0 + array[0].value + array[1].value + ... + array[array.length-1].value - 数组的总和。

我们快到了!最后一部分是实际排序。我们使用sort函数执行此操作(惊喜!)。 sort函数还接受一个函数,该函数具有两个用于迭代数组的参数。但是,您为sort提供的功能必须返回一个数字。 sort使用此函数来比较两个参数;如果输出为正,则第一项为“更大”,而如果输出为负,则第二项“更大”。 (如果输出为零,则它们相等。)sort使用它来按升序对数组进行排序。

在我们的例子中,我们的函数接受两行并返回第一行的总和减去另一行的总和。这意味着如果第一行的总和更大,则ist将在数组中稍后出现,反之亦然。

答案 2 :(得分:1)

为自己比较2种解决方案的效果,可能对其他人有用,可以随意优化:https://jsperf.com/sort-array-by-sum-of-rows/1

我的Firefox,Chrome和Edge只有30-70%的改进(我的ES6代码无法在IE11中编译),因此可读性降低不值得恕我直言:

&#13;
&#13;
const simpleSorter = (arr) => [...arr].sort(
  (a, b) => b.reduce((sum, i) => sum + i.value, 0) -
            a.reduce((sum, i) => sum + i.value, 0)
)

const fasterSorter = (arr) => {
  // note: should use stable sort for equal row scores
  const rowScores = arr.map((row, index) => ({
    score: row.reduce((sum, i, index) => sum + i.value, 0),
    index
  })).sort((a, b) => b.score - a.score)
  return rowScores.map(s => arr[s.index])
}



const rand = () => Math.floor((Math.random() * 10)) + 1
const idGenerator = (() => {
  let i = 0
  return () => i++
})()
const componentGenerator = () => ({a: 'x', value: rand(), id: idGenerator()})
const rowGenerator = () => [...Array(rand())].map(componentGenerator)
const myArray = [...Array(10)].map(rowGenerator)
const prettyPrinter = (arr) => arr.map(i => {
  const temp = i.map(j => j.value)
  return temp.join(' + ') + ' = ' + temp.reduce((a, b) => a + b)
})

const myArraySimpleSorted = simpleSorter(myArray)
const myArrayFasterSorted = fasterSorter(myArray)

console.log('test',
            prettyPrinter(myArraySimpleSorted).toString() ===
            prettyPrinter(myArrayFasterSorted).toString())

console.log('myArray', prettyPrinter(myArray))
console.log('myArraySimpleSorted', prettyPrinter(myArraySimpleSorted))
console.log('myArrayFasterSorted', prettyPrinter(myArrayFasterSorted))
console.log('first in myArray', myArray[0][0])
console.log('first in myArraySimpleSorted', myArraySimpleSorted[0][0])
&#13;
&#13;
&#13;