Souvent, j'étudie certaines questions d'entrevue JavaScript
, tout à coup, j'ai vu une question sur l'utilisation de la fonction reduce
pour trier un Array
, je l'ai lu dans MDN et son utilisation dans certains articles medium
, mais trier un Array
est tellement innovant:
const arr = [91,4,6,24,8,7,59,3,13,0,11,98,54,23,52,87,4];
J'ai beaucoup réfléchi, mais je ne sais pas comment répondre à cette question, comment doit être le reduce
call back
une fonction? quelle est la fonction initialValue
de reduce
? et quels sont les accumulator
et currentValue
de call back
fonction de reduce
?
Et à la fin, cette façon présente-t-elle certains avantages par rapport aux autres algorithmes de tri? Ou est-il utile d'améliorer d'autres algorithmes?
Cela n'a aucun sens d'utiliser réduire ici, mais vous pouvez utiliser un nouveau tableau comme accumulateur et effectuer un tri par insertion avec tous les éléments:
array.reduce((sorted, el) => {
let index = 0;
while(index < array.length && el < array[index]) index++;
sorted.splice(index, 0, el);
return sorted;
}, []);
Voici la version sans réduire:
array.sort((a, b) => a - b);
Maintenant, quelques conseils généraux pour écrire des réducteurs:
comment doit être la fonction de rappel réduit?
Soit vous adoptez une approche avec un accumulateur, puis le réducteur doit appliquer une modification à l'accumulateur en fonction de l'élément actuel et le renvoyer:
(acc, el) => acc
Ou si l'accumulateur et les éléments ont le type sain et sont logiquement égaux, vous n'avez pas besoin de les distinguer:
(a, b) => a + b
quelle est la valeur initiale de la fonction de réduction?
Vous devriez vous demander "Qu'est-ce qui devrait réduire le retour quand il est appliqué sur un tableau vide?"
Maintenant le plus important: quand utiliser réduire? (OMI)
Si vous souhaitez réduire les valeurs d'un tableau en ne seule valeur ou un seul objet.
Array.sort
mute le tableau où utiliser Array.reduce
encourage une fonction pure. Vous pouvez cloner le tableau avant de trier.
Je crois que cette question est conçue pour vous faire penser différemment en imposant des contraintes. Il teste vos connaissances sur le fonctionnement de reduce
et, comme les réponses le montrent, il existe de nombreuses façons d'écorcher un chat. Cela montrera votre saveur personnelle de js en résolvant cela.
J'ai choisi d'utiliser Array.findIndex
et 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, []);
Le test avec l'entrée d'échantillon produit
arr.reduce(sortingReducer, [])
// (17) [0, 3, 4, 4, 6, 7, 8, 11, 13, 23, 24, 52, 54, 59, 87, 91, 98]
Voici un exemple de sorting
un tableau en ordre décroissant utilisant la fonction de réduction.
quelle est la valeur initiale de la fonction de réduction
Dans cette fonction ci-dessous, la valeur initiale est le []
qui est passé comme thisArg
dans la fonction de réduction.
array.reduce(function(acc,curr,currIndex,array){
//rest of the code here
},[]//this is initial value also known as thisArg)
Donc, fondamentalement, un tableau vide est passé et les éléments seront poussés vers ce tableau
L'accumulateur ici est le tableau vide.
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)
Voici une version (imo) plus élégante de la solution de tri par insertion Jonas W . Le rappel crée simplement un nouveau tableau de toutes les valeurs inférieures, la nouvelle et toutes les valeurs supérieures. Évite d'utiliser des boucles ou des index explicites, il est donc plus facile de voir d'un coup d'œil que cela fonctionne correctement.
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
vous oblige à des algorithmes de tri en ligne, où chaque élément du tableau est vu une fois et vous n'avez pas maintenant à l'avance la longueur de votre tableau (notez qu'en utilisant des fermetures vous pourriez donner plus d'informations sur la fonction réductrice, mais ce type de résultat va à l'encontre du but de la question).
le tri par insertion est un exemple évident et facile; Je ne détaillerai pas la mise en œuvre car les autres réponses sont déjà très bonnes à cet égard. Cependant, vous pouvez mentionner quelques optimisations qui pourraient probablement être considérées comme très positives pour l'intervieweur:
Utiliser la recherche binaire pour trouver le point d'insertion peut réduire la complexité d'une étape d'insertion de O(n) à O (log n). Cela s'appelle le tri par insertion binaire: le nombre total de comparaisons sera passer de O (n ^ 2) à O (n log n). Ce ne sera pas plus rapide car le coût réel est dû au "splicing" du tableau de sortie mais si vous aviez des comparaisons coûteuses (disons, trier de longues chaînes par exemple) cela pourrait faire une différence.
Si vous triez un entier, vous pouvez utiliser radix sort et implémenter un algorithme de tri linéaire en ligne avec réduire. C'est assez difficile à mettre en œuvre, mais très adapté à une réduction.
Cependant, réduire n'est pas idéalement destiné au tri. La solution suivante est comme une forEach
ou n'importe quelle fonction de boucle essayant d'être réalisée avec Array.reduce
une fonction.
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);
Veuillez noter que vous pouvez utiliser la fonction native Array.sort pour le tri et pouvez également avoir votre propre fonction de tri personnalisée où vous pouvez définir votre propre algorithme de tri.
Vous pouvez utiliser un tri par insertion:
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)
)
);
Cela créera le tableau de destination une fois, puis effectuera des insertions dans celui-ci à chaque étape de la réduction sans avoir à recréer le tableau de destination.
Il existe des moyens plus efficaces d'implémenter countIfEqual
mais j'ai choisi d'implémenter toutes les fonctions en utilisant reduce
plutôt que d'autres fonctions de tableau.
Vous pouvez utiliser certaines fonctions pour obtenir un tableau si aucun tableau n'est fourni, une fonction qui renvoie un tableau trié en prenant deux paramètres et un rappel de tri qui inclut ce qui précède en utilisant une autre méthode de réduction pour obtenir le résultat partiel d'un tableau trié.
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; }