簡體   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