Four methods that walk through a list and do something with each element, and how to tell which one you need.
All four members of this family go through a list one element at a time and run a piece of code for each element. They differ in what they do with the answer that code gives.
The family
map — changes every element
map gives you a new list with one result for each element. The code inside tells it how to turn an element into its result.
my @prices = 3.50, 12, 0.99, 7.25, 20;
say @prices.map(* * 2);
say @prices.map({ $_ * 2 });
(7 24 1.98 14.5 40) (7 24 1.98 14.5 40)
The two lines do the same. * * 2 is a short way to write a block with one argument: the first * stands for the element, the second one is the multiplication.
The original array does not change. map makes a new list.
grep — keeps some elements
grep asks a yes-or-no question about each element and keeps the elements for which the answer is yes. The elements themselves do not change.
my @prices = 3.50, 12, 0.99, 7.25, 20; say @prices.grep(* > 5);
(12 7.25 20)
Instead of a block, you can give grep a regex or a value. It keeps every element that matches it.
my @cities = 'Riga', 'Rome', 'Oslo', 'Rotterdam', 'Bern', 'Ottawa';
say @cities.grep(/o/);
say @cities.grep('Oslo');
(Rome Oslo Rotterdam) (Oslo)
first — finds one element
first asks the same kind of question as grep, but it stops at the first element that answers yes and gives you that element, not a list.
my @prices = 3.50, 12, 0.99, 7.25, 20; say @prices.first(* > 5); say @prices.first(* > 5, :k); say @prices.first(* > 100);
12 1 Nil
With :k, you get the position of the element instead of the element. When nothing matches, first gives you Nil.
classify — sorts elements into groups
classify runs the code for each element and uses the answer as the name of a group. The result is a hash: one key per group, and an array of the elements in that group.
my @cities = 'Riga', 'Rome', 'Oslo', 'Rotterdam', 'Bern', 'Ottawa';
my %by-letter = @cities.classify(*.substr(0, 1));
for %by-letter.keys.sort -> $letter {
say "$letter: %by-letter{$letter}";
}
B: Bern O: Oslo Ottawa R: Riga Rome Rotterdam
The keys are sorted before printing, because a hash does not keep its keys in any particular order.
Side by side
| Method | The code answers | You get |
|---|---|---|
map |
what to put instead of the element | a list of the same length |
grep |
yes or no | a shorter list of the original elements |
first |
yes or no | one element, or Nil |
classify |
the name of a group | a hash of groups |
A quick test: if the code returns a new value, you want map. If it returns True or False, you want grep or first, depending on whether you need all the matches or one. If it returns a label, you want classify.
Which one?
Which method would you use for each task?
- Get the length of every city name in a list.
- Find the first city in a list whose name is longer than five letters.
- Split a list of words into groups by their number of letters.
- Keep only the prices under 10.
Answers
map:@cities.map(*.chars)gives(4 4 4 9 4 6)for the cities above.first:@cities.first(*.chars > 5)givesRotterdam.classify:@words.classify(*.chars).grep:@prices.grep(* < 10).