views:

969

answers:

3

I'm hopelessly trying to write a method to manipulate an array in ruby. I'm trying to generate all in-order permutations of an array where each item is in turn replaced by an outside item. An example...

Given input:

arr = ["a", "b", "c"]

Desired output:

newArr = [ ["a", "b", "c"], ["a", "b", "*"], ["a", "*", "c"], ["a", "*", "*"], ["*", "b", "c"], ["*", "b", "*"], ["*", "*", "c"], ["*", "*", "*"] ]

Any help would be greatly appreciated. Thank you!

+2  A: 

I'm not sure permutation is the right word. If you count in binary, then you are replacing the things if there is a one. Here's that in Ruby:

def mike(arr, sub)
  format = sprintf("%%0%db", arr.length)
  m = Array.new
  0.upto(2**arr.length-1) { |i|
    bits = sprintf(format, i).split('')
    a = Array.new
    0.upto(arr.length-1) { |j|
      if bits[j] == '0' then
        a << arr[j]
      else
        a << sub
      end
    }
    m[i] = a
  }
  return m
end

arr = ["a", "b", "c"]

p mike(arr, '*')

Is that if-then-else better with a ternary operator?

a <<= bits[j] == '0' ? arr[j] : sub

There must be a cleverer (or, at least more Rubyesque) way to do this, but it seems to produce the desired output.

ETA: Oops! My second and third items don't agree with yours. I guess I don't know what order you mean.

oylenshpeegul
It's order agnostic, should have mentioned. Sorry about that. Order corrected above to avoid future confusion. Thanks oylenshpeegul.
Mike
+1  A: 

Similar to oylenshpeegui's method:

def toggle(arr, sub)
  format = "%0#{arr.length}b"
  (0...2**(arr.length)).to_a.map do |i|
    sprintf(format,i).split('').zip(arr).map { |x| x[0] == "0" ? x[1] : sub }
  end
end

The split/zip combo matches each digit of the binary expansion of the index with the element it is selecting. The map at the end uses the digit to decide if it should return the array element or the substitution.

Bkkbrad
+7  A: 

I don't understand your example order, either, but ignoring that, here's a solution in one line:

(0...(2**a.size)).map {|x| (0...a.size).map {|y| x & 2**y == 0 ? a[y] : val}}
glenn mcdonald
It's order agnostic, should have mentioned. Sorry about that. Order corrected above to avoid future confusion. Thanks glenn -- very elegant solution!
Mike