1

What is the shortest way of getting all K-item combinations of an N-item array where K <= N? I managed to write down the one below :

 > [1,2,3].instance_eval "(1..size).flat_map {|i| self.combination(i).to_a }"

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

Any ideas how to get rid of "instance_eval"? It doesn't seem to be very elagant :\

David Grayson
  • 84,103
  • 24
  • 152
  • 189
boggy
  • 192
  • 2
  • 4

4 Answers4

2

Here's a cool, short way to implement a "power set" function, if order/empty list doesn't matter:

>>> [nil].product(*[1, 2, 3].map { |x| [nil, x] }).map(&:compact)
[[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
Lynn
  • 10,425
  • 43
  • 75
2

I would do something like this:

x = [1,2,3]
1.upto(x.size).flat_map { |i| x.combination(i).to_a }
#=> [[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
spickermann
  • 100,941
  • 9
  • 101
  • 131
1
arr = [1,2,3,4]

(2**arr.size).times.map do |i|
  arr.each_with_index.with_object([]) { |(e,j),a| a << e if i[j] == 1 }
end
  #=> [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3], [4], [1, 4],
  # [2, 4], [1, 2, 4], [3, 4], [1, 3, 4], [2, 3, 4], [1, 2, 3, 4]]

You could, of course, sort the array that is returned however you like. For example:

(2**arr.size).times.map do |i|
  arr.each_with_index.with_object([]) { |(e,j),a| a << e if i[j] == 1 }
end.sort_by { |a| [a.size,a] }
  #=> [[],
  #    [1], [2], [3], [4],
  #    [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4],
  #    [1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4],
  #    [1, 2, 3, 4]] 
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
  • 1
    let me add a comment for how this works: it loops from `0` to `1111` in binary. If size of `arr` is 6, then it is from `0` to `111111`, etc. Then it just check if it is 0 or 1 for the bit, to decide whether to include that element. For example, for `1011` and `arr = [1,2,3,4]`, then add 1, 3, 4 and that makes it `[1, 3, 4]` – nonopolarity Jul 27 '19 at 20:15
  • @太極者無極而生, thanks for the explanation. I normally provide one; don't know why I didn't provide one here. – Cary Swoveland Jul 27 '19 at 20:19
0

One version, hope it's correct.

x = [1, 2, 3]
1.upto(x.size).reduce([]) { |a, i| a + x.permutation(i).map(&:sort).uniq.to_a }
# => [[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
sschmeck
  • 7,233
  • 4
  • 40
  • 67
  • Not the most efficient way of generating combinations: `[1,2,3,4].permutation(3).map(&:sort).uniq == [1,2,3,4].combination(3).to_a #=> true`. – Cary Swoveland Jul 14 '15 at 21:41
  • @CarySwoveland, you are rigtht. I prefer the solution in the comments by niklas-b but I read it afterwards. At this is also a short solution. – sschmeck Jul 15 '15 at 06:17