# Arrays, Hashes and Enumerable — Ruby

Source: https://www.geekswithgeeks.com/en/ruby/c-collections

> Work with arrays and hashes using Enumerable methods.

## Collections with a rich toolbox

**Arrays** are ordered and can hold any objects: `[1, "two", :three]`. **Hashes** map keys to values and preserve insertion order: `{ "Pune" => 2, "Delhi" => 1 }`, with the symbol shorthand `{ city: "Pune", total: 1200 }`, and Ruby 3.1's value omission `{ city:, total: }` uses local variables of the same names. Missing keys return `nil`; `fetch(:key)` raises instead, and `fetch(:key, default)` or `Hash.new(0)` provide defaults. Both include the **`Enumerable`** module, giving dozens of methods: `each`, `map`, `select`/`filter`, `reject`, `find`, `reduce`/`sum`, `count`, `group_by`, `partition`, `sort_by`, `min_by`/`max_by`, `tally`, `each_with_object`, `each_slice`, `zip`, `flat_map`, `any?`, `all?` and `none?`. Methods ending in **`!`** (such as `sort!`, `map!`, `uniq!`) modify the receiver in place; their non-bang versions return new objects. **`&:symbol`** is shorthand for a block that calls that method: `names.map(&:upcase)`. **Lazy enumerators** (`.lazy`) handle large or infinite sequences efficiently.

## Enumerable in action

Grouping, summing and tallying orders with no explicit loops.

```ruby
orders = [
  { id: "o1", city: "Pune",  total: 1200, paid: true },
  { id: "o2", city: "Delhi", total: 300,  paid: false },
  { id: "o3", city: "Pune",  total: 800,  paid: true },
]

paid = orders.select { |o| o[:paid] }
revenue = paid.sum { |o| o[:total] }                       # 2000
by_city = orders.group_by { |o| o[:city] }                  # { "Pune" => [...], "Delhi" => [...] }
city_totals = by_city.transform_values { |list| list.sum { _1[:total] } }   # Pune 2000, Delhi 300
largest = orders.max_by { |o| o[:total] }[:id]              # "o1"
city_counts = orders.map { |o| o[:city] }.tally             # { "Pune" => 2, "Delhi" => 1 }
unpaid, settled = orders.partition { |o| !o[:paid] }

stock = Hash.new(0)
%w[pen ink pen].each { |sku| stock[sku] += 1 }             # { "pen" => 2, "ink" => 1 }

city = "Mumbai"
total = 950
new_order = { city:, total: }                               # value omission (Ruby 3.1+)
puts new_order.fetch(:city)
puts %w[pune delhi].map(&:capitalize).inspect               # ["Pune", "Delhi"]
```

## Bang methods change the receiver

`list.sort` returns a new sorted array; `list.sort!` sorts `list` itself. Also note that some bang methods return `nil` when nothing changed (such as `uniq!`), so do not chain them.

**Quiz:** What does `orders.map(&:id)` do?

- [ ] Deletes ids
- [x] Calls the id method on each element and returns an array of the results
- [ ] Sorts by id
- [ ] It is a syntax error

*Answer:* Calls the id method on each element and returns an array of the results. &:symbol converts the method name into a block that calls it on each element.
