Problem overview
priceList gets an ArrayBuffer[String] of items and an ArrayBuffer[Int] of prices. Both have the same length, from 1 to 100 entries, and the price at each index belongs to the item at the same index. It returns an ArrayBuffer[String] with one line per item, like "1. apple: 3": the item's number, a dot and a space, the item, a colon and a space, and the price. Numbers start at 1 and the lines keep the order of items.
["apple", "bread", "milk"]with[3, 2, 4]gives["1. apple: 3", "2. bread: 2", "3. milk: 4"].["tea"]with[5]gives["1. tea: 5"].["pen", "pen", "ink"]with[2, 3, 0]gives["1. pen: 2", "2. pen: 3", "3. ink: 0"]. Both pens get their own line, and the free ink has price 0.
Approach
Each line needs three things: an item, its price and a number. The item and the price sit at the same position in two buffers, which is what zip pairs up. The number is the position plus one, which is what zipWithIndex adds. Chain them, and one loop sees everything it needs on each pass.
The index starts at 0 and the numbers must start at 1, so the line uses i + 1.
Step by step
- Start with an empty
ArrayBuffer[String]()namedlines. - Loop over
items.zip(prices).zipWithIndex. Each element is a pair of a pair and an index, so the loop pattern is((item, price), i). - Inside, add the line with
+=, using ans"..."string:${i + 1}, a dot and a space,item, a colon and a space,price. - After the loop, return
lines.
The solution in Scala
class Solution {
def priceList(items: ArrayBuffer[String], prices: ArrayBuffer[Int]): ArrayBuffer[String] = {
val lines = ArrayBuffer[String]()
for (((item, price), i) <- items.zip(prices).zipWithIndex) {
lines += s"${i + 1}. $item: $price"
}
return lines
}
}
What items.zip(prices).zipWithIndex builds for the first example is ArrayBuffer(((apple,3),0), ((bread,2),1), ((milk,4),2)). The nesting in the loop pattern follows it: the pair is inside, and the index is outside and second. A flat pattern like (item, price, i) would not match this shape.
In the string, ${i + 1} is needed for the arithmetic, because a plain $i only inserts a name. The other parts, $item and $price, are single names, so they need no braces.
The lines keep the order of items because zip and zipWithIndex both walk the buffers from the first position to the last. The lengths match, as the statement promises, so no price is cut off by zip. If the lengths could differ, the shorter buffer would decide how many lines you get.
Testing
["apple", "bread", "milk"], [3, 2, 4] ArrayBuffer(1. apple: 3, 2. bread: 2, 3. milk: 4)
["tea"], [5] ArrayBuffer(1. tea: 5)
["pen", "pen", "ink"], [2, 3, 0] ArrayBuffer(1. pen: 2, 2. pen: 3, 3. ink: 0)
Another way
You can also loop over the index and read both buffers: for (i <- items.indices) with items(i) and prices(i). It gives the same lines, but the pairs make it harder to read the wrong position. Another form skips the loop: items.zip(prices).map { case (item, price) => s"$item: $price" } builds the lines without numbers.
Common mistakes
- Printing the index as is. The first line would start with
0.instead of1.. - Putting the index first in the pattern.
zipWithIndexgives the item first and the index second. With the names swapped, the line text comes out wrong. - Dropping a repeated item. Two pens get two lines, because each line comes from a position, not from the item's name.
- Returning inside the loop. Return after the loop, once every line has been added.
