Seriously stuck on Ruby array reordering - ruby

I need to reorder an array based on dependent elements. If an element is an array (has dependency), the second element of that array is the dependency, and will be required to come before that array. All dependency information is then removed as we don't need any more, and we return an array with corrected order.
# array1 = [['a'], ['b','c'], ['c','a']]
# ordered = ['a', 'c', 'b']
# logic: c comes before b, a comes before c
Here is my approach which I think is over-engineered:
array1.each_with_index do |ar, i|
# ignore elements without dependencies
if ar.count > 1
# get dependency
dep = ar[1]
# get index for element where this dependency is first
dep_index = array1.index { |a| a.first == dep }
# remove found dependency and store
dep_element = array1.delete_at(dep_index)
# insert found dependency to before current element
array1.insert(i, dep_element)
# delete processed dependency
ar.delete(dep)
end
end
The obvious problem with the above is that as I iterate through the array, elements which have dependencies I haven't processed will be shifted back, yet the loop will only be performed once. So, I introduced a while:
while array1.flatten.count > array1.count
However, my result is ['c', 'a', 'b'].
I have also been tasked to test for self-referential and circular (infinite) dependency loops. Should I have used an Enumerator? Should have I converted array to different structure (objects) to enable easier management of order?

Check out TSort, which comes with the Ruby standard library.
It performs a topological sort, which sounds like what you need. Using your example above:
require 'tsort'
class Hash
include TSort
alias tsort_each_node each_key
def tsort_each_child(node, &block)
fetch(node).each(&block)
end
end
def deps arr
arr.map { |head, *tail| {head => tail} }.reduce(&:merge).tsort
end
deps [['a'], ['b','c'], ['c','a']]
#=> ['a', 'c', 'b']

No need to reinvent the wheel, use Rake:
require 'rake'
ary = [[:a], [:b, :c], [:c, :a]]
ordered = []
ary.each { |head, *tail| task head => tail do ordered << head end }
task( main: ary.map( &:first ) ).invoke
ordered
#=> [:a, :c, :b]
Otherwise, there is an algo for this, but I forgot its name.

Related

Ruby Hash initialised with each_with_object behaving weirdly

Initializing Ruby Hash like:
keys = [0, 1, 2]
hash = Hash[keys.each_with_object([]).to_a]
is behaving weirdly when trying to insert a value into a key.
hash[0].push('a')
# will result into following hash:
=> {0=>["a"], 1=>["a"], 2=>["a"]}
I am just trying to insert into one key, but it's updating the value of all keys.
Yes, that each_with_object is super-weird in itself. That's not how it should be used. And the problem arises precisely because you mis-use it.
keys.each_with_object([]).to_a
# => [[0, []], [1, []], [2, []]]
You see, even though it looks like these arrays are separate, it's actually the same array in all three cases. That's why if you push an element into one, it appears in all others.
Here's a better way:
h = keys.each_with_object({}) {|key, h| h[key] = []}
# => {0=>[], 1=>[], 2=>[]}
Or, say
h = keys.zip(Array.new(keys.size) { [] }).to_h
Or a number of other ways.
If you don't care about hash having this exact set of keys and simply want all keys to have empty array as default value, that's possible too.
h = Hash.new { |hash, key| hash[key] = [] }
All your keys reference the same array.
A simplified version that explains the problem:
a = []
b = a
a.push('something')
puts a #=> ['something']
puts b #=> ['something']
Even though you have two variables (a and b) there is only one Array Object. So any changes to the array referenced by variable a will change the array referenced by variable b as well. Because it is the same object.
The long version of what you are trying to achieve would be:
keys = [1, 2, 3]
hash = {}
keys.each do |key|
hash[key] = []
end
And a shorter version:
[1, 2 ,3].each_with_object({}) do |key, accu|
accu[key] = []
end

Iterating over an array of arrays

def compute(ary)
return nil unless ary
ary.map { |a, b| !b.nil? ? a + b : a }
end
compute([1,2],[3,4])
Can someone please explain to me how compute adds the inner array's values?
To me it seems that calling map on that array of arrays would add the two arrays together, not the inner elements of each array.
map basically iterates over the elements of the object:
foo = [
['a', 'b'],
['c', 'd']
]
foo.map{ |ary| puts ary.join(',') }
# >> a,b
# >> c,d
In this example it's passing each sub-array, which is assigned to ary.
Looking at it a bit differently:
foo.map{ |ary| puts "ary is a #{ary.class}" }
# >> ary is a Array
# >> ary is a Array
Because Ruby lets us assign multiple values at once, that could have been written:
foo.map{ |item1, item2| puts "item1: #{ item1 }, item2: #{ item2 }" }
# >> item1: a, item2: b
# >> item1: c, item2: d
If map is iterating over an array of hashes, each iteration yields a sub-hash to the block:
foo = [
{'a' => 1},
{'b' => 2}
]
foo.map{ |elem| puts "elem is a #{ elem.class }" }
# >> elem is a Hash
# >> elem is a Hash
If map is iterating over a hash, each iteration yields the key/value pair to the block:
foo = {
'a' => 1,
'b' => 2
}
foo.map{ |k, v| puts "k: #{k}, v: #{v}" }
# >> k: a, v: 1
# >> k: b, v: 2
However, if you only give the block a single parameter, Ruby will assign both the key and value to the variable so you'll see it as an array:
foo.map{ |ary| puts "ary is a #{ary.class}" }
# >> ary is a Array
# >> ary is a Array
So, you have to be aware of multiple things that are happening as you iterate over the container, and as Ruby passes the values into map's block.
Beyond all that, it's important to remember that map is going to return a value, or values, for each thing passed in. map, AKA collect, is used to transform the values. It shouldn't be used as a replacement for each, which only iterates. In all the examples above I didn't really show map used correctly because I was trying to show what happens to the elements passed in. Typically we'd do something like:
foo = [['a', 'b'], ['c', 'd']]
foo.map{ |ary| ary.join(',') }
# => ["a,b", "c,d"]
Or:
bar = [[1,2], [3,4]]
bar.collect{ |i, j| i * j }
# => [2, 12]
There's also map! which changes the object being iterated, rather than returns the values. I'd recommend avoiding map! until you're well aware of why it'd be useful to you, because it seems to confuse people no end unless they understand how variables are passed and how Arrays and Hashes work.
The best thing is to play with map in IRB. You'll be able to see what's happening more easily.
I think I figured this out myself.
map selects the first array of the array-of-arrays and pipes it into the block. The variables a and b therefore refer to the first array's inner elements, rather than the first array and the second array in the array-of-arrays.

What are the differences between "each", "foreach", "collect" and "map"? [duplicate]

This question already has answers here:
what's different between each and collect method in Ruby [duplicate]
(7 answers)
Closed 8 years ago.
It seems like there are a lot of ways to loop over an Enumerable in Ruby. Are there any differences between each, foreach, or in collect, map or other similar methods?
Or is there a reason I shouldn't use certain methods in certain situations?
collect/map are equivalent. They differ from each in that each only executes the block for each element, whereas collect/map return an array with the results of calling the block for each element. Another way to put it might be, each is expected to do something with each element, whereas map is expected to transform each element (map it onto something else).
You could use collect or map anywhere each is used, and your code will still work. But it will probably be slightly less efficient because it collects the results in an array, unless your Ruby implementation realizes it doesn't have to bother creating an array because it's never used.
Another reason to use each instead of map or collect is to help out anyone reading your code. If I see each then I can be like okay, we're about to use each element of the data to do something. If I see map then I'm expecting to see new data being created, based on a remapping of the old data.
With regards to map vs. collect I would say it's a matter of preference, but you should pick one and stick with it for consistency.
Using pry and Rubinus (it's easier to read ruby code) take a look for yourself
$ pry
[1] pry(main)> cd Enumerable
[2] pry(Enumerable):1> show-method collect
From: .../rbx-head/kernel/common/enumerable18.rb # line 4:
Owner: Enumerable
Visibility: public
Number of lines: 9
def collect
if block_given?
ary = []
each { |o| ary << yield(o) }
ary
else
to_a
end
end
[3] pry(Enumerable):1> show-method map
From: .../rbx-head/kernel/common/enumerable18.rb # line 4:
Owner: Enumerable
Visibility: public
Number of lines: 9
def collect
if block_given?
ary = []
each { |o| ary << yield(o) }
ary
else
to_a
end
end
[4] pry(Enumerable):1>
So nope not for these two.
map and collect iterate over a collection. For each object it executes your block then collects the result. The each methods do not collect the results
Array#collect (and Array#map) return a new array based on the code passed in the block. Array#each performs an operation (defined by the block) on each element of the array.
I would use collect like this:
array = [1, 2, 3]
array2 = array.collect {|val| val + 1}
array.inspect # => "[1, 2, 3]"
array2.inspect # => "[2, 3, 4]"
And each like this:
array = [1, 2, 3]
array.each {|val| puts val + 1 }
# >> 2
# >> 3
# >> 4
array.inspect # => "[1, 2, 3]"

How to display dynamic case statement in Ruby

How would I write a case statement that would list all elements in an array, allow the user to pick one, and do processing on that element?
I have an array:
array = [ 'a', 'b', 'c', 'd' ]
Ultimately I'd like it to behave like this:
Choices:
1) a
2) b
3) c
4) d
Choice =>
After the user picks 3, I would then do processing based off the choice of the user. I can do it in bash pretty easily.
Ruby has no built-in menu stuff like shell scripting languages do. When doing menus, I favor constructing a hash of possible options and operating on that:
def array_to_menu_hash arr
Hash[arr.each_with_index.map { |e, i| [i+1, e] }]
end
def print_menu menu_hash
puts 'Choices:'
menu_hash.each { |k,v| puts "#{k}) #{v}" }
puts
end
def get_user_menu_choice menu_hash
print 'Choice => '
number = STDIN.gets.strip.to_i
menu_hash.fetch(number, nil)
end
def show_menu menu_hash
print_menu menu_hash
get_user_menu_choice menu_hash
end
def user_menu_choice choice_array
until choice = show_menu(array_to_menu_hash(choice_array)); end
choice
end
array = %w{a b c d}
choice = user_menu_choice(array)
puts "User choice was #{choice}"
The magic happens in array_to_menu_hash:
The [] method of Hash converts an array with the form [ [1, 2], [3, 4] ] to a hash {1 => 2, 3 => 4}. To get this array, we first call each_with_index on the original menu choice array. This returns an Enumerator that emits [element, index_number] when iterated. There are two problems with this Enumerator: the first is that Hash[] needs an array, not an Enumerator. The second is that the arrays emitted by the Enumerator have the elements in the wrong order (we need [index_number, element]). Both of these problems are solved with #map. This converts the Enumerator from each_with_index into an array of arrays, and the block given to it allows us to alter the result. In this case, we are adding one to the zero-based index and reversing the order of the sub-arrays.

Ruby all possible permutations of an array of arrays (one liner?)

Questions similar to this have been asked before on SO, but they're not quite what I need and I can't seem to arrive at my solution through altering/modifying those approaches.
In any case, I have an array of arrays, as follows:
b= [["1"],["2"],["3"],["4"],["5"],["6"]]
(If it makes it easier to arrive at a solution, b can also be a one dimensional array, as follows: ["1","2","3","4","5","6"]. Either type of input works for my needs.)
and I would like to generate the following:
[["123456"],["213456"],["312456"],...]
where each array in the output array is a unique permutation of the six numbers. I would also take it as a single array (e.g., ["123456", "213456",...]). The order of the output isn't particularly important as long as each entry is unique and no number repeats in a string (e.g., "112345" isn't allowed). All 6 numbers must also be used in each entry, so I'm not interested in incremental output like "123", either.
As much as this sounds like it, this isn't a homework problem. I could brute for this thing and get the output I need. I just feel like there has to be a better, more elegant, solution.
With Array#permutation:
permutations = (1..6).to_a.permutation.map(&:join)
# ["123456", "123465", "123546", ..., "654312", "654321"]
Ruby does this natively :)
From the ruby documentation :
a = [1, 2, 3]
a.permutation.to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
a.permutation(1).to_a #=> [[1],[2],[3]]
a.permutation(2).to_a #=> [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]]
a.permutation(3).to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
a.permutation(0).to_a #=> [[]] # one permutation of length 0
a.permutation(4).to_a #=> [] # no permutations of length 4
http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-permutation
You should definitely have a look at Permutation Gem. Example from documentation
perm = Permutation.new(3)
# => #<Permutation:0x57dc94 #last=5, #rank=0, #size=3>
colors = [:r, :g, :b]
# => [:r, :g, :b]
perm.map { |p| p.project(colors) }
# => [[:r, :g, :b], [:r, :b, :g], [:g, :r, :b], [:g, :b, :r], [:b, :r, :g],
# [:b, :g, :r]]
UPDATE
If you are using Ruby > 1.8.6, Array.permutation is built in.
This should do it:
b.permutation.to_a.collect! { |i| i = [i.flatten.join] }

Resources