问题
Often I study some JavaScript interview questions, suddenly I saw a question about usage of reduce function for sorting an Array, I read about it in MDN and the usage of it in some medium articles, But sorting an Array is so Innovative:
const arr = [91,4,6,24,8,7,59,3,13,0,11,98,54,23,52,87,4];
I thought a lot, but I've no idea about how answer this question, how must be the reduce call back function? what is the initialValue of reduce function? and what are the accumulator and currentValue of call back function of reduce?
And at the end, Does this way have some benefits than other sorting algorithms? Or Is it useful to improve other algorithms?
回答1:
It makes no sense to use reduce here, however you could use a new array as an accumulator and do insertion sort with all elements:
array.reduce((sorted, el) => {
let index = 0;
while(index < array.length && el < array[index]) index++;
sorted.splice(index, 0, el);
return sorted;
}, []);
Here is the version without reduce:
array.sort((a, b) => a - b);
Now some general tips for writing reducers:
how must be the reduce call back function?
You either take an approach with an accumulator, then the reducer should apply a modification to the accumulator based on the current element and return it:
(acc, el) => acc
Or if accumulator and the elements have the sane type and are logically equal, you dont need to distinguish them:
(a, b) => a + b
what is the initialValue of reduce function?
You should ask yourself "What should reduce return when it is applied on an empty array?"
Now the most important: When to use reduce? (IMO)
If you want to boil down the values of an array into one single value or object.
回答2:
Array.sort mutates the array where using Array.reduce encourages a pure function. You could clone the array before sorting.
I believe this question is designed to get you thinking differently by enforcing constraints. It tests your knowledge of how reduce works and as the answers show there are many ways to skin a cat. It'll show your personal flavour of js in solving this.
I chose to use Array.findIndex and Array.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, []);
Testing with the sample input produces
arr.reduce(sortingReducer, [])
// (17) [0, 3, 4, 4, 6, 7, 8, 11, 13, 23, 24, 52, 54, 59, 87, 91, 98]
回答3:
Here is an example of the sorting an array in descending order using reduce function.
what is the initialValue of reduce function
In this below function the initial value is the [] which is passed as thisArg in the reduce function.
array.reduce(function(acc,curr,currIndex,array){
//rest of the code here
},[]//this is initial value also known as thisArg)
So basically an empty array is passed and the elements will be be pushed to this array
The accumulator here is the empty array.
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)
回答4:
reduce constraints yourself to online sorting algorithms, where each element in the array is seen once and you do not now in advance the length of your array (note that using closures you could give more information to the reducing function but this kinds of defeats the purpose of the question).
insertion sort is an obvious and easy example; I won't detail the implementation as the other answers are already very good in this regard. However you can mention a few optimizations that might probably seen as very positive to the interviewer:
Use binary search to find the insertion point can reduce the complexity of an insertion step from O(n) to O(log n). This is called binary insertion sort: the overall number of comparisons will go from O(n^2) to O(n log n). This won't be faster because the real cost is due to "splicing" the output array but if you had expensive comparisons (say, sorting long strings for instance) it could make a difference.
If you sort integer, you can use radix sort and implement a linear online sorting algorithm with reduce. This is quite trickier to implement, but very suited to a reduction.
回答5:
Here's an (imo) more elegant version of Jonas W's insertion sort solution. The callback just builds a new array of all lower values, the new one and all higher values. Avoids using explicit loops or indices, so it's easier to see at a glance that it works correctly.
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, []))
回答6:
Though, reduce is not ideally meant for the sorting. The following solution is just like a forEach or any loop function trying to be achieved with Array.reduce function.
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);
Please note, you can use the native function Array.sort for sorting and can also have your own custom sort function where you can define your own sorting algorithm.
回答7:
You could use some functions for getting an array if not an array is supplied, a function which returns a sorted array by taking two parameters and a sort callback which includes the above by using another reduce method for getting a part result of a sorted array.
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; }
回答8:
You can use an insertion sort:
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)
)
);
This will create the destination array once and then perform insertions into it at each step of the reduce without having to recreate the destination array.
There are more efficient ways of implementing countIfEqual but I chose to implement all the functions using reduce rather than other array functions.
来源:https://stackoverflow.com/questions/50245957/sorting-array-with-javascript-reduce-function