2

What I have

a = [1,2,3,4]
 => [1, 2, 3, 4] 

b = a.combination(2).to_a
 => [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] 

b.each_slice(2).to_a
 => [[[1, 2], [1, 3]], [[1, 4], [2, 3]], [[2, 4], [3, 4]]] 

What I'm trying to achieve is a unique combination

=> [[[1, 2], [3, 4]], [[1, 4], [2, 3]], [[1, 3], [2, 4]]]

I have tried with permutation, flatten, &c. but cannot find the magic ruby code!

Edit :

The answer above is like

b = a.combination(2).to_a
=> [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

To be more precise.

From

a = [1,2,3,4,5,6]

how to get

=> [[[1, 2], [3, 4], [5, 6]], [[1, 3], [2, 5], [4, 6]], [[1, 4], [2, 6], [3, 5]], [[1, 5], [2, 4], [3, 6]], [[1, 6], [2, 3], [4, 5]]]

which is 5 arrays of uniq values (1,2,3,4,5,6):

[1, 2], [3, 4], [5, 6]
[1, 3], [2, 5], [4, 6]
[1, 4], [2, 6], [3, 5]
[1, 5], [2, 4], [3, 6]
[1, 6], [2, 3], [4, 5]

You seem to have changed the question. Originally you wanted an array of arrays, each of >which had a pair of arrays. Now you want triplets?

Yes, because the first exemple with [1,2,3,4] was too easy, and the answer doesn't fit with a more complex array like [1,2,3,4,5,6] and so one.

Kev
  • 118,037
  • 53
  • 300
  • 385
highlight
  • 31
  • 3
  • The question is extremely vague. – sawa Jun 30 '11 at 15:49
  • You seem to have changed the question. Originally you wanted an array of arrays, each of which had a pair of arrays. Now you want triplets? – Andy Jun 30 '11 at 17:20

4 Answers4

1

this gets you most of the way there i think

[1,2,3,4].combination(2).inject([]){|arr,r| arr << (Hash[*r]); arr}

if you take the first and last element from this array iteratively you get what you were after

def con(h, arr = []) 
  arr <<[h.delete(h.first).to_a.flatten, h.delete(h.last).to_a.flatten]
  con(h, arr) unless h.empty?
  p arr
end

#=> [[[1, 2], [3, 4]], [[1, 3], [2, 4]], [[1, 4], [2, 3]]]
chrispanda
  • 3,204
  • 1
  • 21
  • 23
0

Well, it ain't pretty, but it works. combination takes a block.

a = [1,2,3,4]
ans = []

a.combination(2) do |i|
  a.combination(2) do |j|
    x = [i, j]
    y = x.flatten
    next if y.uniq != y
    ans << x
  end
end

puts ans.inspect

EDIT: made it slightly less ugly.

Andy
  • 1,480
  • 14
  • 17
0

Added this as another answer because it's really a slightly different question - and a lot harder!

 def accept(a)
   0.upto(a.size-1){|i| return false unless a[i] == a[i].sort
   return false if (i > 0 && a[i][0] <= a[i-1][0])}
   true
end

 x=[1,2,3,4,5,6].permutation.inject([]){|arr, per| arr<< per.in_groups_of(2); arr}
 arr = x.inject([]){|arr,y| arr << y if accept(y); arr}
 p arr

Not very pretty, but does what you want for any size of array I think

chrispanda
  • 3,204
  • 1
  • 21
  • 23
  • This give a lot of duplicate array like [1, 2]. What i wanted is this array uniq, like in the exemple i wrote. – highlight Jul 01 '11 at 09:54
  • actually, the print statement in the last line prints out what you want - edited to make it clearer - int the console this is swamped by the output that follows – chrispanda Jul 01 '11 at 10:03
0

Finally i find a solution without permutation, uniq, combination, flatten :)

    a = [1,2,3,4,5,6]
    count = a.count

    totalloop = count - 1
    arrayperloop = count / 2
    rounds = []

    for round in 0...totalloop
        for i in 0...arrayperloop

            x = (round + i) % (count - 1)
            y = (count - 1 - i + round) % (count - 1)

            if i == 0
              y = count - 1
            end

            rounds<<[x + 1, y + 1]
        end
    end

rounds.each_slice(arrayperloop).to_a give me what i wanted

    [[[1, 6], [2, 5], [3, 4]], [[2, 6], [3, 1], [4, 5]], [[3, 6], [4, 2], [5, 1]], [[4, 6], [5, 3], [1, 2]], [[5, 6], [1, 4], [2, 3]]]

Not so ugly ! And always work if we add n*2 integer to the array.

highlight
  • 31
  • 3