- Understand Block Return Values
- Be able to use
max_by,their opposites, and
- Return Value
Given the array
kardashians = ["Khloe", "Kim", "Kris", "Kourtney"], use
- Find all the Kardashians with 3 or more letters
- Create a new array with all the names upcased
Exploration: Block Return Values
How do the enumerables we know so far work under the hood? Work through this section with a partner to explore this question. Before you run each code snippet, try to predict the output.
Run each of the following examples and think about how map works.
numbers = [1, 2, 3, 4] doubled = numbers.map do |number| number * 2 end p doubled
numbers = [1, 2, 3, 4] doubled = numbers.map do |number| number * 2 0 end p doubled
numbers = [1, 2, 3, 4] doubled = numbers.map do |number| 0 number * 2 end p doubled
Run each of the following examples and think about how find_all works.
numbers = [1, 2, 3, 4] evens = numbers.find_all do |number| number.even? end p evens
numbers = [1, 2, 3, 4] evens = numbers.find_all do |number| number.even? true end p evens
numbers = [1, 2, 3, 4] evens = numbers.find_all do |number| true number.even? end p evens
numbers = [1, 2, 3, 4] evens = numbers.find_all do |number| false end p evens
numbers = [1, 2, 3, 4] evens = numbers.find_all do |number| 1 + 1 number.even? "This is a string" ["This", "is", "an", "array"] true end p evens
Check for Understanding
Discuss the following questions with a partner and answer in your notebooks:
- How does
mapknow what value to map to?
- How does
find_allknow which elements will be returned?
- How do you think
findknows which element to return?
- Read the descriptions for
find_allin the Ruby Docs Enumerables Page. How are these descriptions similar/different to your answers to the first 3 questions?
min / max
What would we do if we wanted to get the largest thing out of an array?
Let’s think about how we would do that with .each.
nums = [1,3,9,2,5] greatest = nums.first nums.each do |num| if num > greatest greatest = num end end puts greatest
That’s cool. But there’s a much easier way - we can make Ruby do the work for us.
nums = [1,3,9,2,5] puts nums.max
And what if we wanted to take the smallest? You’d just use
TURN & TALK: All the other enumerables have a do block; these don’t but are still considered enumerables - why?
You can use these methods for strings as well as numbers. Letters have a sort of intrinsic values on their own.
What do I mean? open up a pry session in your terminal and type in,
"a" > "b"
We can see that the string,
"a" is in fact, less than the string
Knowing this we can do some cool things like grabbing the “lowest” alphabetical string within an array.
["Brian", "Mike", "Amy"].min
This code, here, it’ll return us
"Amy". Be careful - this is NOT straight up comparing the length of the strings - it’s comparing the value of each string! Try running this:
["hello", "hi", "hey"].min
"zzz" > "aaaa" true
If we swap out the min for a max, what will we get?
This is normally where we would have you try this on your own, but I’m not going to insult your intelligence.
min_by / max_by
Let’s go back to our code to find the largest value using
each. This time we’ll use an array of Strings as the example:
names = ["Khloe", "Kim", "Kris", "Kourtney"] greatest = names.first names.each do |name| if name > greatest greatest = name end end puts greatest
In this example, we use the greater than operator
> to compare our Strings. We just saw that by default Ruby compares Strings by the letter value. What if we want it to do something different, for example, compare by the length of the String? We’d have to do this:
names = ["Khloe", "Kim", "Kris", "Kourtney"] greatest = names.first names.each do |name| if name.length > greatest.length greatest = name end end puts greatest
The idea here is that we are overriding how we are comparing the elements in the array. We can do this even easier with
names = ["Khloe", "Kim", "Kris", "Kourtney"] greatest = names.max_by do |name| name.length end
max_by takes whatever the last line of code executed in the block is and uses that to find the max element. In this case, it uses the length of each String to determine what the max should be.
This is quite handy when we make our own objects and we want to find the max/min based on some criteria. Imagine we have a class
Person that stores a name and age:
class Person attr_reader :name, :age def initialize(name, age) @name = name @age = age end end
And let’s store some instances of
Person in an Array:
people =  people << Person.new("Sofia", 4) people << Person.new("Scarlett", 9) people << Person.new("Stella", 8)
What if we wanted to get the max Person by age? If you call
people.max, Ruby will tell you it doesn’t know how to compare two
So let’s walk this process out and look at how we would do this with .each. It’s a lot like how we would implement .max or .min.
def max_by(people) oldest = people.first people.each do |person| if person.age > oldest.age oldest = person end end oldest end
This is very similar to our original implementation. The main difference is that instead of comparing the objects and determining which is “greater or lesser”, we are comparing their attributes to each other.
And so, the max_by enumerable works similarly.
greatest = people.max_by do |person| # use the max_by enumerable to iterate person.age # max_by will return the greatest person.age end
We are iterating over the array, looking at each item in the array, looking at the attribute and then returning the entire object that has the largest value that we want.
Another way to see it, to use this enumerable, we list our criteria for searching in the block, and the enumerable will simply give us the matching object.
We can also grab the first alphabetically here.
people.min_by do |person| person.name end
It doesn’t have to be an array of objects, it can be an array of arrays. We’re talking about a collection of things that might hold more than one piece of data.
So let’s simplify the problem.
people = [ ["Sofie", 4], ["Scarlett", 9], ["Stella", 8] ] people.max_by do |person| person # index 1 is the integer/age end
To find the youngest person, I would use the
We’ve worked on grabbing the largest thing or smallest thing out of a collection, and that’s great. But the next logical step is to sort them.
Essentially, it works very similarly to the enumerable methods that we’ve been talking about so far. The main difference is that instead of returning a single object, it returns an array of sorted objects, sorted by the criteria that you select IN ASCENDING ORDER.
Just like with
max, ruby Arrays have a method
sort that will sort based on the default comparison. For Integers, this is simply sorting based on value:
[2,4,3,1].sort => [1,2,3,4]
For Strings, it will sort alphabetically:
["Brian", "Mike", "Amy"].sort => ["Amy", "Brian", "Mike"]
Just like with
min, sometimes the default comparison isn’t good enough, and we want to override how ruby will compare our objects. For instance, if we want to sort Strings based on their length:
names = ["Khloe", "Kim", "Kris", "Kourtney"] sorted = names.sort_by do |name| name.length end
And now, for something completely different.
We’re going to look at one of the enumerables that returns a simple true or false. This is indicated by the method ending with a
Let’s look at the name of this enumerable,
all?. Under the hood, it’s an enumerable with a conditional in the block. If every item in a collection returns
true when going through the block, the entire method returns
true. Otherwise, it will return
[1,1,1,1].all? do |num| num == 1 end
["dog","cat","pig","hippopotamus"].all? do |word| word.length == 3 end
This would return false.
Given what you just learned about
all? - can make an educated guess about what
class Person attr_reader :name, :age def initialize(name, age) @name = name @age = age end end kardashians =  kardashians << Person.new("Kourtney", 39) kardashians << Person.new("kim", 37) kardashians << Person.new("Kris", 62) kardashians << Person.new("Khloe", 33)
Write code, without using /#each, to:
- Get the youngest member
- Get the person with the longest name
- Sort them by age
- Check if all their names start with a
- Check if any of them are younger than 18
- Sort them by the last letter of their name, descending (Should be “Kourtney”, “Kris”, “Kim”, “Khloe”)
- Check if exactly one Person is neither “Kris” nor younger than 38 (Should be true)
- Name all the enumerables you know. What do they each return?
In the enums-exercises, complete the following: