2

With this function I generate needed ranges:

first_index = 0
last_index = 3
ranges = []

while first_index != last_index
  while last_index != 0
    if first_index < last_index 
      ranges << (first_index..last_index)
    end 
      last_index -= 1
  end
  first_index += 1
  last_index = 3
end 

p ranges

The output is:

[0..3, 0..2, 0..1, 1..3, 1..2, 2..3]

I need to revert the output of the nested while loop, after it finishes. So in this example, I need:

 [0..3, 0..2, 0..1].reverse 
 [1..3, 1..2].reverse
 [2..3].reverse (wouldn't make any different on this, though)

The output I would get is:

[0..1, 0..2, 0..3, 1..2, 1..3, 2..3]

Can I invoke reverse somehow in that function? last_index could be any integer. I used 3 just to shorten the output.

2
  • 4
    (0..3).to_a.combination(2).map { |a, b| a..b } returns the ranges in the expected order. Commented Apr 5, 2017 at 15:34
  • 1
    @Stefan Well thats a great solution in just a few seconds. If you could write an answer with a brief explanation, I would accept it and maybe this will help someone else as well, some day.. Commented Apr 5, 2017 at 15:40

2 Answers 2

7

So the output I would get:

=> [0..1, 0..2, 0..3, 1..2, 1..3, 2..3]

This is exactly what Array#combination returns:

a = [0, 1, 2, 3]
a.combination(2).to_a
#=> [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]

To get ranges:

a.combination(2).map { |a, b| a..b }
#=> [0..1, 0..2, 0..3, 1..2, 1..3, 2..3]

However, note that the documentation says: (emphasis added)

The implementation makes no guarantees about the order in which the combinations are yielded.

So you might want to explicitly sort the result:

 a.combination(2).sort
 #=> [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
Sign up to request clarification or add additional context in comments.

1 Comment

… and the sort does work because Arrays are guaranteed to be compared lexicographically. (I think it's mentioned in the docs for Array#<=>.)
1

If the order is critical, you could use an intermediary array.

first_index = 0
last_index = 3
ranges = []
sub_ranges = []

while first_index != last_index
    while last_index != 0
        if first_index < last_index 
            sub_ranges << (first_index..last_index)
        end 
            last_index -= 1
    end
    ranges << sub_ranges.reverse
    sub_ranges = []
    first_index += 1
    last_index = 3
end
ranges.flatten!
p ranges

It is a far shot, but on large number array manipulations become relatively expensive. You could rely more on numerical work. Alternatively, you just like this one more:

first_index = 0
last_index = 3
ranges = []

y = first_index + 1

while first_index != last_index
    while y <= last_index
      ranges << (first_index..y)
      y += 1
    end
    first_index += 1
    y = first_index + 1
end

1 Comment

A more idiomatic implementation: (0...3).flat_map { |a| (a+1..3).map { |b| a..b } } with 0 being first_index and 3 being last_index

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.