繁体   English   中英

使用 JavaScript 减少函数对数组进行排序

[英]Sorting Array with JavaScript reduce function

经常研究一些JavaScript试题,突然看到一个关于reduce函数对Array进行排序的问题,我在MDN上看过,在一些medium文章中也看到过它的用法,但是排序Array太有创意了:

const arr = [91,4,6,24,8,7,59,3,13,0,11,98,54,23,52,87,4];

我想了很多,但我不知道如何回答这个问题, reduce call back函数必须如何? reduce函数的initialValue是多少? reduce call back函数的accumulatorcurrentValue是什么?

最后,这种方式是否比其他排序算法有一些好处? 或者改进其他算法有用吗?

在这里使用 reduce 是没有意义的,但是您可以使用一个新数组作为累加器并对所有元素进行插入排序:

array.reduce((sorted, el) => {
  let index = 0;
  while(index < sorted.length && el < sorted[index]) index++;
  sorted.splice(index, 0, el);
  return sorted;
}, []);

这是没有减少的版本:

array.sort((a, b) => a - b);

现在一些编写减速器的一般提示:

减少回调函数必须如何?

您要么采用累加器的方法,然后减速器应根据当前元素对累加器应用修改并返回它:

(acc, el) => acc

或者,如果 accumulator 和元素具有健全的类型并且在逻辑上是相等的,则不需要区分它们:

 (a, b) => a + b

reduce 函数的初始值是多少?

你应该问自己“当它应用于空数组时,什么应该减少回报?”

现在最重要的是:什么时候使用reduce? (海事组织)

如果您想将数组的值归结为一个值或对象

Array.sort改变数组,其中使用Array.reduce鼓励纯函数。 您可以在排序之前克隆数组。

我相信这个问题旨在通过强制约束让您以不同的方式思考。 它测试您对reduce工作原理的了解,正如答案所示,有很多方法可以给猫剥皮。 它将在解决此问题时展示您个人的 js 风格。

我选择使用Array.findIndexArray.splice

const sortingReducer = (accumulator, value) => {
  const nextIndex = accumulator.findIndex(i => value < i );
  const index = nextIndex > -1 ? nextIndex : accumulator.length;
  accumulator.splice(index, 0, value);
  return accumulator;
}

const input = [5,4,9,1];
const output = input.reduce(sortingReducer, []);

使用样本输入进行测试会产生

arr.reduce(sortingReducer, [])
// (17) [0, 3, 4, 4, 6, 7, 8, 11, 13, 23, 24, 52, 54, 59, 87, 91, 98]

这是使用reduce函数按降序对数组进行sorting的示例。

什么是reduce函数的initialValue

在这个下面的函数中,初始值是[] ,它在 reduce 函数中作为thisArg传递。

 array.reduce(function(acc,curr,currIndex,array){
        //rest of the code here
        },[]//this is initial value also known as thisArg)

所以基本上传递了一个空数组,元素将被推送到这个数组

这里的累加器是空数组。

 const arr = [91, 4, 6, 24, 8, 7, 59, 3, 13, 0, 11, 98, 54, 23, 52, 87, 4]; var m = arr.reduce(function(acc, cur) { // this arrVar will have the initial array let arrVar = arr; // get the max element from the array using Math.max // ... is spread operator var getMaxElem = Math.max(...arrVar); // in the accumulator we are pushing the max value acc.push(getMaxElem); // now need to remove the max value from the array, so that next time it // shouldn't not be considered // splice will return a new array // now the arrVar is a new array and it does not contain the current // max value arrVar = arrVar.splice(arrVar.indexOf(getMaxElem), 1, '') return acc; }, []); console.log(m)

这是Jonas W 的插入排序解决方案的(imo)更优雅的版本。 回调只是构建一个包含所有较低值、新值和所有较高值的​​新数组。 避免使用显式循环或索引,因此更容易一目了然地看到它是否正常工作。

 const insertValue = (arr, value) => [...arr.filter(n => n <= value), value, ...arr.filter(n => n > value)] const testArr = [91, 4, 6, 24, 8, 7, 59, 3, 13, 0, 11, 98, 54, 23, 52, 87, 4] console.log(testArr.reduce(insertValue, []))

reduce在线排序算法的约束,其中数组中的每个元素都被看到一次,并且您现在没有预先确定数组的长度(请注意,使用闭包可以为减少函数提供更多信息,但这种方式违背了目的的问题)。

插入排序是一个明显而简单的例子; 我不会详细说明实现,因为其他答案在这方面已经非常好。 但是,您可以提及一些可能对面试官非常积极的优化:

  • 使用二分查找找到插入点可以将插入步骤的复杂度从 O(n) 降低到 O(log n)。 这称为二元插入排序:比较的总数将从 O(n^2) 到 O(n log n)。 这不会更快,因为真正的成本是由于“拼接”输出数组,但如果您进行了昂贵的比较(例如,对长字符串进行排序),它可能会有所作为。

  • 如果对整数进行排序,则可以使用基数排序并使用reduce 实现线性在线排序算法。 这实现起来相当棘手,但非常适合减少。

如果没有提供数组,您可以使用一些函数来获取数组,一个函数通过接受两个参数返回一个排序的数组,一个排序回调包括上面的通过使用另一个 reduce 方法获取排序数组的部分结果。

 const getArray = a => Array.isArray(a) ? a : [a], order = (a, b) => a < b ? [a, b] : [b, a], sort = (a, b) => getArray(a).reduce((r, v) => r.concat(order(r.pop(), v)), [b]), array = [91, 4, 6, 24, 8, 7, 59, 3, 13, 0, 11, 98, 54, 23, 52, 87, 4]; console.log(array.reduce(sort));
 .as-console-wrapper { max-height: 100% !important; top: 0; }

您可以使用插入排序:

 let array = [91, 4, 6, 24, 8, 7, 59, 3, 13, 0, 11, 98, 54, 23, 52, 87, 4]; let countIfLess = (array,v)=> array.reduce((c,n)=>n<v?c+1:c,0); let countIfEqual = (array,v)=> array.reduce((c,n)=>n==v?c+1:c,0); console.log( array.reduce( (a,v,i,array)=>( a[countIfLess(array,v) + countIfEqual(a,v)]=v, a ), new Array(array.length) ) );

这将创建一次目标数组,然后在 reduce 的每一步执行插入操作,而无需重新创建目标数组。

有更有效的方法来实现countIfEqual但我选择使用reduce而不是其他数组函数来实现所有函数。

一个糟糕的排序算法可以用 ES6 写成一行:

const sorter = (xs, x) => xs.slice(-1)[0] >= x ? [x, ...xs] : [...xs, x];

如果当前元素大于或等于先前排序列表的最后一个元素,则将其附加到末尾,否则附加到开头。

[3,4,1].reduce(sorter,[]).reduce(sorter,[])

//returns [1,3,4]

除了最简单的数组之外,它需要几个应用程序才能返回排序。

但它最终会到达那里。 这需要递归!

 const arr = [91,4,6,24,8,7,59,3,13,0,11,98,54,23,52,87,4]; const sorter2 =(as) => as.reduce( (xs, x) => x >= xs.slice(-1)[0] ? [...xs, x] : xs[0] < x ? sorter2([x, ...xs]) : [x, ...xs], [], ); const result = sorter2(arr); console.log(result.join(', '));

当当前值大于已处理数组的最后一个值时,它被追加。 如果它小于第一个元素,则将其添加到前面。 仅当它既不在当前值之前也不在当前值之后,并且累积的数组通过递归调用再次排序。 该方法应该等同于插入排序(请注释!)。

但是,reduce 并不是排序的理想选择。 下面的解决方案就像一个forEach或任何试图用Array.reduce函数实现的循环函数。

 var arr = [91,4,6,24,8,7,59,3,13,0,11,98,54,23,52,87,4]; arr = arr.reduce(function(acc,val){ if(acc.length) { var temp = []; while(acc[acc.length -1] > val) { temp.push(acc.pop()); } acc.push(val); while(temp.length) { acc.push(temp.pop()); } } else { acc.push(val); } return acc; }, []); console.log(arr);

请注意,您可以使用本机函数Array.sort进行排序,也可以拥有自己的自定义排序函数,您可以在其中定义自己的排序算法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM