0

Within Ruby, I've been searching for an elegant single-line statement to assign multiple variables (but not all) a common value returned from an array via method call.

The crux of the objective is to convert a two-line statement such as:

a, x = 1, 2
y = x

Into: a, x, y = 1, 2, 2 without hard-coding the repeated values. So a gets one value (1) while x and y both share a common value (2).

To extend the example into a use-case, let's say instead of directly assigning the values 1, 2, we assign our values via an array returned from a method call:

a, x = 7.divmod 5  # array [1,2] is unpacked via parallel assignment
y = x

This has the same result as the first code sample and we could replace the Integers with variables to make the assignment dynamic.

Can you recommend techniques to consolidate this assignment into a single line? Here are a couple options I've considered so far:

n = 7 # number
m = 5 # divided by

# Option 1) Store array via inline-variable assignment and append last
#           element to align values for the parallel assignment
a, x, y = (t = n.divmod(m)) << t[-1]

# Option 2) Use Array#values_at to repeat some elements
a, x, y = n.divmod(m).values_at 0,1,1

For those new to parallel assignment, this SO post covers standard usage.

Community
  • 1
  • 1
Cam
  • 921
  • 7
  • 13
  • I'm not sure you can do exactly what you're asking. One thing you could do to condense the right side of your `=` is use the `*` operator, which appends copies of an array to itself. For example, `[1, 2] * 3 => [1, 2, 1, 2, 1, 2]`. Thus `a, b, c = [1] + [2] * 2` is equivalent to `a, b, c = 1, 2, 2`. I can't really think of many situations where it would be quicker to do this, but it's there if you want it. – lwassink Jul 11 '16 at 22:26
  • Do you want the single line or do you want readable code? – Sergio Tulentsev Jul 12 '16 at 02:31
  • @Sergio, ideally both. It may be that one-line options add more complexity than they're worth here. Edge case challenges do occasionally dig up new techniques though, so thought I'd post. – Cam Jul 12 '16 at 10:31
  • 1
    `a, x = 7.divmod 5; y = x` Voilà! One line *and* readable. (Asking for one-liners in Ruby is kind of pointless, since any and all Ruby code can always be written on one line, since newline as separator can always be replaced with either semicolon, a keyword, or whitespace.) – Jörg W Mittag Jul 12 '16 at 10:45

1 Answers1

1

Another option

a, x, y = 7.divmod(5).tap {|a| a << a[-1]}
nikkypx
  • 1,925
  • 17
  • 12
  • Honestly, that additional assignment looks best of all options. Certainly better than this one. :) – Sergio Tulentsev Jul 12 '16 at 02:30
  • @nikkypx use of `#tap` is an improvement over my first option, as it doesn't introduce a new variable into scope. It also can enable use of a method like `#fill` (which doesn't appear to provide a self-reference within its block): `a, x, y, z = 7.divmod(5).tap {|a| a.fill(2,2){a[1]} }` – Cam Jul 12 '16 at 10:36