Ένας οδηγός για τη μέθοδο μείωσης στο Javascript

Η μέθοδος μείωσης του JavaScript είναι ένας από τους ακρογωνιαίους λίθους του λειτουργικού προγραμματισμού.Ας εξερευνήσουμε πώς λειτουργεί, πότε πρέπει να το χρησιμοποιήσετε και μερικά από τα ωραία πράγματα που μπορεί να κάνει.

Μια βασική μείωση

Χρησιμοποιήστε το όταν : Έχετε μια σειρά από ποσά και θέλετε να τα προσθέσετε όλα.

const euros = [29.76, 41.85, 46.5]; const sum = euros.reduce((total, amount) => total + amount); sum // 118.11

Πως να το χρησιμοποιήσεις:

  • Σε αυτό το παράδειγμα, η μείωση δέχεται δύο παραμέτρους, το συνολικό και το τρέχον ποσό.
  • Η μέθοδος μείωσης περιστρέφεται σε κάθε αριθμό στον πίνακα, όπως θα έκανε σε ένα βρόχο.
  • Όταν ξεκινά ο βρόχος, η συνολική τιμή είναι ο αριθμός στην άκρη αριστερά (29,76) και το τρέχον ποσό είναι εκείνο δίπλα του (41,85).
  • Σε αυτό το συγκεκριμένο παράδειγμα, θέλουμε να προσθέσουμε το τρέχον ποσό στο σύνολο.
  • Ο υπολογισμός επαναλαμβάνεται για κάθε ποσό στον πίνακα, αλλά κάθε φορά που η τρέχουσα τιμή αλλάζει στον επόμενο αριθμό στον πίνακα, κινείται δεξιά.
  • Όταν δεν υπάρχουν ακόμη αριθμοί στον πίνακα, η μέθοδος επιστρέφει τη συνολική τιμή.

Η έκδοση ES5 της μεθόδου μείωσης σε JavaScript

Εάν δεν έχετε χρησιμοποιήσει ποτέ σύνταξη ES6, μην αφήσετε το παραπάνω παράδειγμα να σας εκφοβίσει. Είναι ακριβώς το ίδιο με το γράψιμο:

var euros = [29.76, 41.85, 46.5]; var sum = euros.reduce( function(total, amount){ return total + amount }); sum // 118.11

Χρησιμοποιούμε constαντί για varκαι αντικαθιστούμε τη λέξη functionμε ένα "παχύ βέλος" ( =>) μετά τις παραμέτρους και παραλείπουμε τη λέξη "επιστροφή".

Θα χρησιμοποιήσω τη σύνταξη ES6 για τα υπόλοιπα παραδείγματα, καθώς είναι πιο περιεκτική και αφήνει λιγότερο χώρο για σφάλματα.

Εύρεση μέσου όρου με τη μέθοδο μείωσης σε JavaScript

Αντί να καταγράψετε το άθροισμα, θα μπορούσατε να διαιρέσετε το άθροισμα με το μήκος του πίνακα πριν επιστρέψετε μια τελική τιμή.

Ο τρόπος για να γίνει αυτό είναι να επωφεληθείτε από τα άλλα επιχειρήματα στη μέθοδο μείωσης Το πρώτο από αυτά τα επιχειρήματα είναι το ευρετήριο . Όπως και το for-loop, το ευρετήριο αναφέρεται στον αριθμό των φορών που ο μειωτής περιτύλιξε τον πίνακα. Το τελευταίο όρισμα είναι ο ίδιος ο πίνακας .

const euros = [29.76, 41.85, 46.5]; const average = euros.reduce((total, amount, index, array) => { total += amount; if( index === array.length-1) { return total/array.length; }else { return total; } }); average // 39.37

Χάρτης και φιλτράρισμα ως μειώσεις

Εάν μπορείτε να χρησιμοποιήσετε τη λειτουργία μείωσης για να φτύσετε έναν μέσο όρο, τότε μπορείτε να τη χρησιμοποιήσετε με όποιον τρόπο θέλετε.

Για παράδειγμα, θα μπορούσε να διπλασιάσει το σύνολο, ή το μισό από κάθε αριθμό πριν από την προσθήκη τους μαζί, ή να χρησιμοποιήσετε μια εντολή if μέσα στο μειωτήρα για να προσθέσετε μόνο αριθμούς που είναι μεγαλύτερο από 10. Το σημείο μου είναι ότι η Μειώστε Μέθοδος Σε Javascript για να σας δίνει μια μίνι CodePen όπου μπορείτε να γράψετε όποια λογική θέλετε. Θα επαναλάβει τη λογική για κάθε ποσό στον πίνακα και στη συνέχεια θα επιστρέψει μία μόνο τιμή.

Το θέμα είναι ότι δεν χρειάζεται πάντα να επιστρέφετε ούτε μία τιμή. Μπορείτε να μειώσετε έναν πίνακα σε έναν νέο πίνακα.

Για παράδειγμα, ας μειώσουμε έναν πίνακα ποσών σε έναν άλλο πίνακα όπου κάθε ποσό διπλασιάζεται. Για να γίνει αυτό, πρέπει να ορίσουμε την αρχική τιμή του συσσωρευτή μας σε έναν κενό πίνακα.

Η αρχική τιμή είναι η τιμή της συνολικής παραμέτρου κατά την έναρξη της μείωσης. Μπορείτε να ορίσετε την αρχική τιμή προσθέτοντας ένα κόμμα ακολουθούμενο από την αρχική τιμή σας μέσα στις παρενθέσεις αλλά μετά τις σγουρές τιράντες (με έντονους χαρακτήρες στο παρακάτω παράδειγμα ).

const average = euros.reduce((total, amount, index, array) => { total += amount return total/array.length }, 0);

Σε προηγούμενα παραδείγματα, η αρχική τιμή ήταν μηδέν και έτσι το παρέλειψα. Παραλείποντας την αρχική τιμή, το σύνολο θα προεπιλεγεί στο πρώτο ποσό του πίνακα.

Ορίζοντας την αρχική τιμή σε έναν κενό πίνακα, μπορούμε στη συνέχεια να ωθήσουμε κάθε ποσό στο σύνολο . Εάν θέλουμε να μειώσουμε έναν πίνακα τιμών σε έναν άλλο πίνακα όπου κάθε τιμή διπλασιάζεται, πρέπει να ωθήσουμε το ποσό * 2. Στη συνέχεια επιστρέφουμε το σύνολο όταν δεν υπάρχουν περισσότερα ποσά για να ωθήσουμε.

const euros = [29.76, 41.85, 46.5]; const doubled = euros.reduce((total, amount) => { total.push(amount * 2); return total; }, []); doubled // [59.52, 83.7, 93]

Δημιουργήσαμε έναν νέο πίνακα όπου κάθε ποσό διπλασιάζεται. Θα μπορούσαμε επίσης να φιλτράρουμε αριθμούς που δεν θέλουμε να διπλασιάσουμε προσθέτοντας μια δήλωση if μέσα στον μειωτή μας.

const euro = [29.76, 41.85, 46.5]; const above30 = euro.reduce((total, amount) => { if (amount > 30) { total.push(amount); } return total; }, []); above30 // [ 41.85, 46.5 ]

Αυτές οι λειτουργίες είναι οι μέθοδοι χαρτών και φίλτρων που ξαναγράφονται ως μέθοδος μείωσης.

Για αυτά τα παραδείγματα, θα ήταν πιο λογικό να χρησιμοποιείτε χάρτη ή φίλτρο, επειδή είναι απλούστερο στη χρήση. Το όφελος από τη χρήση της μείωσης εμφανίζεται στο παιχνίδι όταν θέλετε να χαρτογραφήσετε και να φιλτράρετε μαζί και έχετε πολλά δεδομένα για να τα διαβάσετε.

Εάν κάνετε αλυσίδα χάρτη και φιλτράρετε μαζί, κάνετε τη δουλειά δύο φορές. Φιλτράρετε κάθε τιμή και στη συνέχεια χαρτογραφείτε τις υπόλοιπες τιμές. Με τη μείωση μπορείτε να φιλτράρετε και μετά να χαρτογραφήσετε με ένα μόνο πάσο.

Χρησιμοποιήστε το χάρτη και το φίλτρο, αλλά όταν αρχίσετε να συνδέετε πολλές μεθόδους μαζί γνωρίζετε τώρα ότι είναι πιο γρήγορο να μειώσετε τα δεδομένα.

Δημιουργία μέτρησης με τη μέθοδο μείωσης σε JavaScript

Χρησιμοποιήστε το όταν : Έχετε μια συλλογή αντικειμένων και θέλετε να μάθετε πόσα από όλα τα είδη είναι στη συλλογή.

const fruitBasket = ['banana', 'cherry', 'orange', 'apple', 'cherry', 'orange', 'apple', 'banana', 'cherry', 'orange', 'fig' ]; const count = fruitBasket.reduce( (tally, fruit) =>  , {}) count // { banana: 2, cherry: 3, orange: 3, apple: 2, fig: 1 }

Για τον υπολογισμό των στοιχείων σε έναν πίνακα, η αρχική τιμή μας πρέπει να είναι ένα κενό αντικείμενο και όχι ένας κενός πίνακας όπως ήταν στο τελευταίο παράδειγμα.

Εφόσον πρόκειται να επιστρέψουμε ένα αντικείμενο, μπορούμε πλέον να αποθηκεύσουμε ζεύγη τιμών-κλειδιών στο σύνολο.

fruitBasket.reduce( (tally, fruit) => { tally[fruit] = 1; return tally; }, {})

Στο πρώτο μας πέρασμα, θέλουμε το όνομα του πρώτου κλειδιού να είναι η τρέχουσα τιμή μας και θέλουμε να του δώσουμε μια τιμή 1.

Αυτό μας δίνει ένα αντικείμενο με όλα τα φρούτα ως κλειδιά, το καθένα με τιμή 1. Θέλουμε να αυξηθεί η ποσότητα κάθε φρούτου εάν επαναληφθούν.

Για να το κάνουμε αυτό, στο δεύτερο βρόχο ελέγξουμε εάν το σύνολο μας περιέχει ένα κλειδί με τον τρέχοντα καρπό του μειωτή. Αν δεν το κάνουμε τότε το δημιουργούμε. Εάν ισχύει, τότε αυξάνουμε το ποσό κατά ένα.

fruitBasket.reduce((tally, fruit) => { if (!tally[fruit]) { tally[fruit] = 1; } else { tally[fruit] = tally[fruit] + 1; } return tally; }, {});

I rewrote the exact same logic in a more concise way up top.

Flattening an array of arrays with the Reduce Method In JavaScript​​

We can use reduce to flatten nested amounts into a single array.

We set the initial value to an empty array and then concatenate the current value to the total.

const data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; const flat = data.reduce((total, amount) => { return total.concat(amount); }, []); flat // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

More often than not, information is nested in more complicated ways. For instance, lets say we just want all the colors in the data variable below.

const data = [ {a: 'happy', b: 'robin', c: ['blue','green']}, {a: 'tired', b: 'panther', c: ['green','black','orange','blue']}, {a: 'sad', b: 'goldfish', c: ['green','red']} ];

We’re going to step through each object and pull out the colours. We do this by pointing amount.c for each object in the array. We then use a forEach loop to push every value in the nested array into out total.

const colors = data.reduce((total, amount) => { amount.c.forEach( color => { total.push(color); }) return total; }, []) colors //['blue','green','green','black','orange','blue','green','red']

If we only need unique number then we can check to see of the number already exists in total before we push it.

const uniqueColors = data.reduce((total, amount) => { amount.c.forEach( color => { if (total.indexOf(color) === -1){ total.push(color); } }); return total; }, []); uniqueColors // [ 'blue', 'red', 'green', 'black', 'orange']

Piping with Reduce

An interesting aspect of the reduce method in JavaScript is that you can reduce over functions as well as numbers and strings.

Let’s say we have a collection of simple mathematical functions. these functions allow us to increment, decrement, double and halve an amount.

function increment(input) { return input + 1;} function decrement(input) { return input — 1; } function double(input) { return input * 2; } function halve(input) { return input / 2; }

For whatever reason, we need to increment, then double, then decrement an amount.

You could write a function that takes an input, and returns (input + 1) * 2 -1. The problem is that we know we are going to need to increment the amount three times, then double it, then decrement it, and then halve it at some point in the future. We don’t want to have to rewrite our function every time so we going to use reduce to create a pipeline.

A pipeline is a term used for a list of functions that transform some initial value into a final value. Our pipeline will consist of our three functions in the order that we want to use them.

let pipeline = [increment, double, decrement];

Instead of reducing an array of values we reduce over our pipeline of functions. This works because we set the initial value as the amount we want to transform.

const result = pipeline.reduce(function(total, func) { return func(total); }, 1); result // 3

Because the pipeline is an array, it can be easily modified. If we want to decrement something three times, then double it, decrement it , and halve it then we just alter the pipeline.

var pipeline = [ increment, increment, increment, double, decrement, halve ];

The reduce function stays exactly the same.

Silly Mistakes to avoid

If you don’t pass in an initial value, reduce will assume the first item in your array is your initial value. This worked fine in the first few examples because we were adding up a list of numbers.

If you’re trying to tally up fruit, and you leave out the initial value then things get weird. Not entering an initial value is an easy mistake to make and one of the first things you should check when debugging.

Another common mistake is to forget to return the total. You must return something for the reduce function to work. Always double check and make sure that you’re actually returning the value you want.

Tools, Tips & References

  • Everything in this post came from a fantastic video series on egghead called Introducing Reduce. I give Mykola Bilokonsky full credit and I am grateful to him for everything I now know about using the Reduce Method In JavaScript​. I have tried to rewrite much of what he explains in my own words as an exercise to better understand each concept. Also, it’s easier for me to reference an article, as opposed to a video, when I need to remember how to do something.
  • The MDN Reduce documentation labels what I called a total the accumulator. It is important to know this because most people will refer to it as an accumulator if you read about it online. Some people call it prev as in previous value. It all refers to the same thing. I found it easier to think of a total when I was learning reduce.
  • If you would like to practice using reduce I recommend signing up to freeCodeCamp and completing as many of the intermediate algorithms as you can using reduce.
  • If the ‘const’ variables in the example snippets are new to you I wrote another article about ES6 variables and why you might want to use them.
  • I also wrote an article called The Trouble With Loops that explain how to use map() and filter() if the are new to you.

Ευχαριστώ για την ανάγνωση! Εάν θέλετε να ειδοποιηθείτε όταν γράφω ένα νέο άρθρο, εισαγάγετε το email σας εδώ.

Και αν σας άρεσε το άρθρο, μοιραστείτε το στα κοινωνικά μέσα, ώστε να μπορούν να το βρουν και άλλοι.