# Intermediate Enumerables

## Learning Goals

• Understand Block Return Values
• Be able to use `max`, `max_by,` their opposites, and `sort_by` appropriately.

Available here

## Vocabulary

• Enumerable
• Iterate/Iteration
• Return Value
• Block

## Warm Up

Given the array `kardashians = ["Khloe", "Kim", "Kris", "Kourtney"]`, use `find`, `find_all`, or `map` to:

1. Find all the Kardashians with 3 or more letters
2. Find `"Kris"`
3. 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.

#### map

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
``````

#### find_all

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:

1. How does `map` know what value to map to?
2. How does `find_all` know which elements will be returned?
3. How do you think `find` knows which element to return?
4. Read the descriptions for `map`, `find`, and `find_all` in 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 `.min` instead.

TURN & TALK: All the other enumerables have a do block; these don’t but are still considered enumerables - why?

## Comparing Strings

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 `"b"`.

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 `max_by`:

``````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

: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 `Person` objects.

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 `min_by` method.

## sort

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"]
``````

## sort_by

Just like with `max` and `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
``````

## all?

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 `false`.

Example:

``````[1,1,1,1].all? do |num|
num == 1
end
``````

This returns `true`.

``````["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 `any?`, `none?`, and `one?` do/return?

## Practice

``````class Person
: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:

1. Get the youngest member
2. Get the person with the longest name
3. Sort them by age
4. Check if all their names start with a `k` (case insensitive)
5. Check if any of them are younger than 18
6. Sort them by the last letter of their name, descending (Should be “Kourtney”, “Kris”, “Kim”, “Khloe”)
7. Check if exactly one Person is neither “Kris” nor younger than 38 (Should be true)

## Wrap Up

• Name all the enumerables you know. What do they each return?

### For Homework:

In the enums-exercises, complete the following:

• find_using_max_by_test.rb
• sort_by_test.rb
• all_pattern_test.rb
• all_test.rb
• any_pattern_test.rb
• any_test.rb
• none_pattern_test.rb
• none_test.rb
• one_pattern_test.rb
• one_test.rb

## Lesson Search Results

### Showing top 10 results 