1

I have this code snippet, a bucket in this case is just an array within a larger array:

def Dict.get_slot(aDict, key, default=nil)
    # Returns the index, key, and value of a slot found in a bucket.
    bucket = Dict.get_bucket(aDict, key)

    bucket.each_with_index do |kv, i|
        k, v = kv
        if key == k
            return i, k, v
        end
    end

    return -1, key, default
end

The two variables called k and v are set to the contens of kv. But how can this work, when kv only contains one value at a time?

I wrote this into another file:

    bucket = ['key', 'value']
    key = 'key'
    bucket.each_with_index do |kv, i|
        k, v = kv
        if key == k
        puts k, v, i
        end
    end

And then the v variable was empty:

key

0

My question is, why does multiple assignment work in the first example, but not in the second?

Magnus
  • 31
  • 1
  • 6

2 Answers2

6

bucket is a dictionary, simply put: a list of pairs of values, not just a list of values. Consider:

bucket.each do |kv|
  # kv is an array: [some_k, some_v]
  k, v = kv
  # k is some_k
  # v is some_v
end

bucket.each_with_index do |kv, i|
  # kv is again an array: [some_k, some_v]
  k, v = kv
  # k is some_k
  # v is some_v
end

As a side note, this kind of "pattern matching" can also be used in nested form for the block parameters directly:

bucket.each_with_index do |(k, v), i|
  # k is some_k
  # v is some_v
end
Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168
  • This is from "Learn Ruby the Hard Way" exercise 39. so basically kv is shortcut for (k,v)? – myself Jun 08 '16 at 19:49
  • `kv` will contain a two element array with the key and the value. To extract the key and the value into variables the extra step `k, v = kv` is needed. If you write `(k,v)` the array is directly destructured and the key and value will be assigned to `k` and `v` respectively. The extra step is not necessary in this case. – Patrick Oscity Jun 08 '16 at 21:20
1

When you're calling bucket.each_with_index it first acts on 'key', then 'value'

You could try nested arrays, so in this example each member of the array is an array with two items.

irb(main):012:0> [['a','b'],['c','d']].each_with_index{|x,i|puts "#{i}: #{x}"}
0: ["a", "b"]
1: ["c", "d"]

You can then identify these by index

irb(main):019:0> [['a','b'],['c','d']].each_with_index{|x,i|puts "#{i}: #{x[0]} - #{x[1]}"}
0: a - b
1: c - d

Or set these as values with the syntax you used:

irb(main):020:0> [['a','b'],['c','d']].each_with_index{|x,i| a,b = x ; puts "#{i}: #{a} -  #{b}"}
0: a - b
1: c - d

Without the one liners:

bucket = [
  ['a','b'],
  ['c','d']
]
bucket.each_with_index do |x, index|
  k, v = x
  puts index
  puts "#{k} = #{v}"
end
AJFaraday
  • 2,411
  • 1
  • 16
  • 39