Combine multiple strings in array 1 to form array 2 - ruby

I have an array of strings "A" and a target string "B". The B string
contains only characters from a to z and does not contain spaces. I need a ruby function
that returns the array of strings which represents all possible ways to form B from elements of Array A. The order in which combinations are returned is irrelevant. Words from A can be used multiple times.
Example:
A = [‘x’, ‘y’, ‘z’, 'yz',‘xy’, ‘xyz’]
B = ‘xyz’
method(A, B) => [x y z, x yz, xyz, xy z]
I looked into permutation method but can't get it to work.

Here's a recursive solution that doesn't iterate through the permutations:
def find_possible_combinations( list, str )
list.select { |a| str.start_with? a }.map do |a|
if a == str
[a]
else
find_possible_combinations( list-[a], str.slice(a.length..-1) )
.map { |c| "#{a} #{c}" }
end
end.flatten
end
puts "#{find_possible_combinations( %w( x y z yz xy xyz ), 'xyz' )}"
Output:
["x y z", "x yz", "xy z", "xyz"]

A = %w[x y z yz xy xyz]
B = "xyz"
SIZE = B.size
def combos
(1..SIZE).inject([]) {|c, n| c.concat(combos_by_size(n))}.reject(&:empty?)
end
# Select all combinations of size n having SIZE characters
def combos_by_size(n)
A.combination(n).to_a.inject([]) {|c, a| c.concat(matching_combos(a)) if a.join.size == SIZE; c}.reject(&:empty?)
end
# Select all matching combinations using all elements of array a
def matching_combos(a)
a.permutation.to_a.select {|p| p.join == B}
end
combos # => [["xyz"], ["x", "yz"], ["xy", "z"], ["x", "y", "z"]]

Related

Ruby map! does not change contained variable

Suppose I have an object x, and an array y=[x] which contains x. If I manipulate x, then y does not change:
x = 1 # => 1
y = [x] # => [1]
x = x+1 # => 2
x # => 2
y # => [1]
and if I change y, then x does not change.
x = 1 # => 1
y = [x] # => [1]
y.map!{|a| a+1} # => [2]
y # => [2]
x # => 1
Is there a way to have them change in parallel? It feels like when I map! over an array the underlying values should change.
first of all
x = x + 1
will create a new variable with an old name x
x = 1
y = [x]
x.object_id
# 3
y[0].object_id
# 3
x = x + 1
x.object_id
# 5
y[0].object_id
# 3
Second, numbers are immutable objects in Ruby (and Strings, for example, are mutable). So you just can't do what you want using number objects in Ruby. What you can do is slightly more obscure. You can create your own mutable object (container for a number) and use a reference to this object in your array.
class MutableNumber
attr_accessor :n
def initialize(n)
#n = n
end
end
x = MutableNumber.new(1)
y = [x]
y[0].n
#=> 1
x.n += 1
y[0].n
#=> 2
You can go little bit further and to add more magic here to mimic numbers
class MutableNumber
attr_accessor :n
def initialize(n)
#n = n
end
def method_missing(m, *args, &blk)
#n = #n.send(m, *args, &blk)
self
end
end
x = MutableNumber.new(1)
y = [x]
y[0].n
#=> 1
x += 1
y[0].n
#=> 2
But I would not encourage you to do any of this.
Just stick to the idea that numbers are immutable. And overall you should be careful with mutability.
map! is mutable for the array, not for its elements. It means that the array gets new values in place (i.e. it's assigned to original array). Elements inside the array are not mutated, but replaced by new elements.
If you want to change old values, you can iterate using each and call mutating methods on each element. You can see this with array of strings:
a = "a"; b = "b"; aa = [a, b]
#=> ["a", "b"]
aa.map!{|e| e.capitalize }
#=> ["A", "B"]
[a, b]
#=> ["a", "b"]
a = "a"; b = "b"; aa = [a, b]
#=> ["a", "b"]
aa.each{|e| e.capitalize! }
#=> ["A", "B"]
[a, b]
#=> ["A", "B"]
Note that it won't work for immutable objects and numbers are immutable, as #fl00r explained in his answer.

Ruby - Spaceship operator won't work in a block for .sort

I'm getting an error when attempting to use the spaceship operator with non alpha-numeric characters in the sort function.
word = "out-classed"
letters = word.downcase.split('')
letters.sort! do |x, y|
if y < 'a'
next
else
value = x <=> y
end
end
I'm getting ArgumentError: comparison of String with String failed, and I'm almost positive this happens with the spaceship operator and not the < comparison.
The interesting part is that when I do this same comparison in irb outside of the context of a sort block, the comparison works. It also works when the word variable only consists of letters.
Can anybody help me to understand why this doesn't work in this specific context alone?
If you attempt to sort a collection, x<=>y must return 0, 1 or -1 for every pair of elements of the collection. If <=> is defined artificially for some pairs (e.g., 'a'<=>'-' #=> 0 and '-'<=>'a' #=> 0), your sort may return erroneous results.
This is because sort algorithms do not necessarily evaluate all pairs of elements in the collection. If, for example, it finds that:
'a' <=> 'b' #=> 0
and
'b' <=> 'c' #=> 0
it will conclude that:
`a` <=> `c` #=> 0
because the collection being sorted must satisfy transitivity: x <== z if x <= y and y <= z.
For example, if the collection is the array ['z', '-', 'a'] and it finds that 'z' <= '-' and '-' <= 'a', it will conclude that 'z' <= 'a' (and not evaluate 'z' <=> 'a').
That's why:
['z', '-', 'a'].sort { |x,y| p [x,y]; (y < 'a') ? 0 : x<=>y }
#-> ["z", "-"]
#-> ["-", "a"]
#=> ["z", "-", "a"]
doesn't work. You have two choices:
Remove the offending elements before sorting:
['z', '-', 'a'].select { |c| ('a'..'z').cover?(c) }.
sort { |x,y| (y < 'a') ? 0 : x<=>y }
#=> ["a", "z"]
or sort all elements of the collection:
['z', '-', 'a'].sort
#=> ["-", "a", "z"]
If the collection contains non-comparable elements (e.g., [1,2,'cat']), you only choice is to remove elements from the array until all remaining elements are comparable.
Instead of next you need to return 0, 1 or -1. Try this:
word = "out-classed"
letters = word.downcase.split('')
letters.sort! do |x, y|
if y < 'a'
0
else
value = x <=> y
end
end
Your problem lies here
if y < 'a'
next
else
...
sort method expects you to return a comparison value between every pair, so when you call next without returning anything, it says that comparison failed.
Try e.g. this:
if y < 'a'
1
else
value = x <=> y
end

Ruby: How to access each element of an array inside a hash where hash key is the array I want to access

I have:
Array1 = [x, y, z]
Array2 = [m, n]
a = b
hash1 = {Array1 => val1,
Array2 => val2,
a => c
}
How to iterate inside each element of Array1, Array2 inside the hash1?
hash1.each do |t|
t[0] #gives me Array1 as a string. I need [x,y,z]
end
It don't give you a string. It give you the correct array.
{
[1,2] => 'a'
}.each{|t| puts t[0].class}
# prints array
{
[1,2] => 'a'
}.each{|t| puts t[0][0]}
# prints 1
Note that you are doing each on a hash. You can deconstruct the key-value pair giving two variables to the block, like this:
{a:1, b:2}.each { |k,v| p k; p v }
#prints :a
#prints 1
#prints :b
#prints 2
Something like this
hash1.keys.each do |k|
if k.is_a?(Array)
k.each do |v|
.. Do something here ..
end
end
end
Just replace the Do something here with the code you want, and v will be the value in the array

How do I find .index of a multidimensional array

Tried web resources and didnt have any luck and my visual quick start guide.
If I have my 2d/multidimensional array:
array = [['x', 'x',' x','x'],
['x', 'S',' ','x'],
['x', 'x',' x','x']]
print array.index('S')
it returns nil
So then I go and type:
array = ['x', 'S',' ','x']
print array.index('S')
it returns the value I am looking for 1
My first guess something is being called wrong in the .index() and it needs two arguments one for both row and column? Anyways how do I make .index work for a multidimensional array? This is step one for solving my little maze problem
This will do it:
array = [['x', 'x',' x','x'],
['x', 'S',' ','x'],
['x', 'x',' x','x']]
p array.index(array.detect{|aa| aa.include?('S')}) # prints 1
If you also want 'S's index in the sub array you could:
row = array.detect{|aa| aa.include?('S')}
p [row.index('S'), array.index(row)] # prints [1,1]
You can use the method Matrix#index:
require 'matrix'
Matrix[*array].index("S")
#=> [1, 1]
a.each_index { |i| j = a[i].index 'S'; p [i, j] if j }
Update: OK, we can return multiple matches. It's probably best to utilize the core API as much as possible, rather than iterate one by one with interpreted Ruby code, so let's add some short-circuit exits and iterative evals to break the row into pieces. This time it's organized as an instance method on Array, and it returns an array of [row,col] subarrays.
a = [ %w{ a b c d },
%w{ S },
%w{ S S S x y z },
%w{ S S S S S S },
%w{ x y z S },
%w{ x y S a b },
%w{ x },
%w{ } ]
class Array
def locate2d test
r = []
each_index do |i|
row, j0 = self[i], 0
while row.include? test
if j = (row.index test)
r << [i, j0 + j]
j += 1
j0 += j
row = row.drop j
end
end
end
r
end
end
p a.locate2d 'S'
You could find first in which is the absolute position by flattening the array:
pos = array.flatten.index('S')
Then get the number of columns per row:
ncols = array.first.size
then
row = pos / ncols
col = pos % ncols
Non-Ruby specific answer: You're trying to print 'S' in both examples, but only the latter has 'S' in the array. The first has ['x', 'S', ' ', 'x']. What you will need to do (If Ruby doesn't do this for you) is look at each member in the array and search that member for 'S'. If 'S' is contained in that member then print it.
array = [['x', 'x',' x','x'],
['x', 'S',' ','x'],
['x', 'x',' x','x']]
class Array
def my_index item
self.each_with_index{|raw, i| return i if raw.include? item}
return
end
end
p array.my_index("S") #=>1
p array.my_index("Not Exist Item") #=> nil
Specifies both indexes of the first occurrence of element for one pass on subarrays
a = [[...],[...],[...],[...]]
element = 'S'
result_i = result_j = nil
a.each_with_index do|row, i|
if (j = row.index(element))
result_i, result_j = i, j
break
end
end

make an object behave like an Array for parallel assignment in ruby

Suppose you do this in Ruby:
ar = [1, 2]
x, y = ar
Then, x == 1 and y == 2. Is there a method I can define in my own classes that will produce the same effect? e.g.
rb = AllYourCode.new
x, y = rb
So far, all I've been able to do with an assignment like this is to make x == rb and y = nil. Python has a feature like this:
>>> class Foo:
... def __iter__(self):
... return iter([1,2])
...
>>> x, y = Foo()
>>> x
1
>>> y
2
Yep. Define #to_ary. This will let your object be treated as an array for assignment.
irb> o = Object.new
=> #<Object:0x3556ec>
irb> def o.to_ary
[1, 2]
end
=> nil
irb> x, y = o
=> [1,2]
irb> x
#=> 1
irb> y
#=> 2
The difference between #to_a and #to_ary is that #to_a is used to try to convert
a given object to an array, while #to_ary is available if we can treat the given object as an array. It's a subtle difference.
Almost:
class AllYourCode
def to_a
[1,2]
end
end
rb = AllYourCode.new
x, y = *rb
p x
p y
Splat will try to invoke to_ary, and then try to invoke to_a. I'm not sure why you want to do this though, this is really a syntactical feature that happens to use Array in its implementation, rather than a feature of Array.
In other words the use cases for multiple assignment are things like:
# swap
x, y = y, x
# multiple return values
quot, rem = a.divmod(b)
# etc.
name, age = "Person", 100
In other words, most of the time the object being assigned from (the Array) isn't even apparent.
You can't redefine assignment, because it's an operator instead of a method. But if your AllYourCode class were to inherit from Array, your example would work.
When Ruby encounters an assignment, it looks at the right hand side and if there is more than one rvalue, it collects them into an array. Then it looks at the left hand side. If there is one lvalue there, it is assigned the array.
def foo
return "a", "b", "c" # three rvalues
end
x = foo # => x == ["a", "b", "c"]
If there is more than one lvalue (more specifically, if it sees a comma), it assigns rvalues successively and discards the extra ones.
x, y, z = foo # => x == "a", y == "b", z == "c"
x, y = foo # => x == "a", y == "b"
x, = foo # => x == "a"
You can do parallel assignment if an array is returned, too, as you have discovered.
def bar
["a", "b", "c"]
end
x = bar # => x == ["a", "b", "c"]
x, y, z = bar # => x == "a", y == "b", z == "c"
x, y = bar # => x == "a", y == "b"
x, = bar # => x == "a"
So in your example, if rb is an Array or inherits from Array, x and y will be assigned its first 2 values.

Resources