2

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
  • What do you need this for? The new array size will grow exponentially with the original size, so you should be careful with what you pass in. Commented Jan 29, 2009 at 0:52
  • @Ben - Only passing in arrays of 3-5 items. Difficult to explain what it's for but it's not processed real-time -- so, I'm not too worried about performance. Commented Jan 29, 2009 at 1:02

3 Answers 3

8

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}}
Sign up to request clarification or add additional context in comments.

1 Comment

It's order agnostic, should have mentioned. Sorry about that. Order corrected above to avoid future confusion. Thanks glenn -- very elegant solution!
2

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.

1 Comment

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

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.

Comments

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.