I have the following code:
a = ["Cat", "Dog", "Mouse"]
s = ["and", "&"]
I want to merge the array s into array a which would give me:
["Cat", "and", "Dog", "&", "Mouse"]
Looking through the Ruby Array and Enumerable docs, I don't see such a method that will accomplish this.
Is there a way I can do this without iterating through each array?
You can do that with:
a.zip(s).flatten.compact
This won't give a result array in the order Chris asked for, but if the order of the resulting array doesn't matter, you can just use a |= b. If you don't want to mutate a, you can write a | b and assign the result to a variable.
See the set union documentation for the Array class at http://www.ruby-doc.org/core/classes/Array.html#M000275.
This answer assumes that you don't want duplicate array elements. If you want to allow duplicate elements in your final array, a += b should do the trick. Again, if you don't want to mutate a, use a + b and assign the result to a variable.
In response to some of the comments on this page, these two solutions will work with arrays of any size.
If you don't want duplicate, why not just use the union operator :
new_array = a | s
s.inject(a, :<<)
s #=> ["and", "&"]
a #=> ["Cat", "Dog", "Mouse", "and", "&"]
It doesn't give you the order you asked for, but it's a nice way of merging two arrays by appending to the one.
Here's a solution that allows interleaving multiple arrays of different sizes (general solution):
arr = [["Cat", "Dog", "Mouse", "boo", "zoo"],
["and", "&"],
["hello", "there", "you"]]
first, *rest = *arr; first.zip(*rest).flatten.compact
=> ["Cat", "and", "hello", "Dog", "&", "there", "Mouse", "you", "boo", "zoo"]
It's not exactly elegant, but it works for arrays of any size:
>> a.map.with_index { |x, i| [x, i == a.size - 2 ? s.last : s.first] }.flatten[0..-2]
#=> ["Cat", "and", "Dog", "&", "Mouse"]
To handle the situation where both a & s are not of the same size:
a.zip(s).flatten.compact | s
.compact will remove nil when a is larger than s
| s will add the remaining items from s when a is smaller than s
How about a more general solution that works even if the first array isn't the longest and accepts any number of arrays?
a = [
["and", "&"],
["Cat", "Dog", "Mouse"]
]
b = a.max_by(&:length)
a -= [b]
b.zip(*a).flatten.compact
=> ["Cat", "and", "Dog", "&", "Mouse"]
One way to do the interleave and also guarantee which one is the biggest array for the zip method, is to fill up one of the arrays with nil until the other array size. This way, you also guarantee which element of which array will be on first position:
preferred_arr = ["Cat", "Dog", "Mouse"]
other_arr = ["and","&","are","great","friends"]
preferred_arr << nil while preferred_arr.length < other_arr.length
preferred_arr.zip(other_arr).flatten.compact
#=> ["Cat", "and", "Dog", "&", "Mouse", "are", "great", "friends"]
Interleave 2D array of any size
arr = [["Cat", "Dog", "Mouse"],
["and", "&"],
["hello", "there", "you", "boo", "zoo"]]
max_count = arr.map(&:count).max
max_count.times.map{|i| arr.map{|a| a[i]}}.flatten.compact
#=> ["Cat", "and", "hello", "Dog", "&", "there", "Mouse", "you", "boo", "zoo"]
A very clear way to merge multiple arrays is to unpack them into one array. This works in practically the same way for many languages, so I'd prefer this method due to its simplicity and developer familiarity with it.
a = ["Cat", "Dog", "Mouse"]
s = ["and", "&"]
[*a, *s]
#=> ["Cat", "Dog", "Mouse", "and", "&"]
def merge_and_interleave(arr_a, arr_b)
final_arr = []
until arr_a.empty? && arr_b.empty?
final_arr << arr_a.shift unless arr_a.empty?
final_arr << arr_b.shift unless arr_b.empty?
end
final_arr
end
arr = [0, 1]
arr + [2, 3, 4]
//outputs [0, 1, 2, 3, 4]
Related
I am trying to find the index of the first and second instance of a string variable. I want to be able to use any predefined string variable but when I try to do that it gives me an error. I want to be able to declare multiple string variables like ss, aa, ff, etc and use them in place of xx. Can someone help me out?
#aa is a predefined array
xx = "--help--"
find_xx_instance = aa.each_with_index.select{|i,idx| i =~ /xx/}
#/--help--/works but not /xx/
find_xx_instance.map! {|i| i[1]}
#gives me info between the first two instances of string
puts aa[find_xx_instance[0]+1..find_xx_instance[1]-1]
As far as I understand, you just need to pass variable to regular expression. Try this:
find_xx_instance = aa.each_with_index.select{|i,idx| i =~ /#{xx}/}
I have assumed you are given an array of strings, arr, a string str, and an integer n, and wish to return an array a of n elements i, where i is the index of the ith+1 instance of str in arr.
For example:
arr = %w| Now is the time for the Zorgs to attack the Borgs |
#=> ["Now", "is", "the", "time", "for", "the", "Zorgs", "to", "attack", "the", "Borgs"]
str = "the"
nbr = 2
This is one way:
b = arr.each_index.select { |i| arr[i]==str }
#=> [2, 5, 9]
b.first(nbr)
#=> [2, 5]
which can be written
arr.each_index.select { |i| arr[i]==str }.first(nbr)
This For small problems like this one, that's fine, but if arr is large, it would be better to terminate the calculations after nbr instances of str have been found. We can do that by creating a Lazy enumerator:
arr.each_index.lazy.select { |i| arr[i]==str }.first(nbr)
#=> [2, 5]
Here's a second example that clearly illustrates that lazy is stopping the calculations after nbr strings str in arr have been found:
(0..Float::INFINITY).lazy.select { |i| arr[i] == str }.first(nbr)
#=> [2, 5]
I want to get the coordinates of every occurrence of an object stored in an array of arrays. If I have an array:
array = [["foo", "bar", "lobster"], ["camel", "trombone", "foo"]]
and an object "foo", I want to get:
[[0,0], [1,2]]
The following will do this, but it's elaborate and ugly:
array.map
.with_index{
|row,row_index| row.map.with_index {
|v,col_index| v=="foo" ? [row_index,col_index] : v
}
}
.flatten(1).find_all {|x| x.class==Array}
Is there a more straightforward way to do this? This was asked before, and produced a similarly inelegant solution.
Here's a slightly more elegant solution. I have:
Used flat_map instead of flattening at the end
Used .each_index.select instead of .map.with_index and then having to strip non-arrays at the end, which is really ugly
Added indentation
array.flat_map.with_index {|row, row_idx|
row.each_index.select{|i| row[i] == 'foo' }.map{|col_idx| [row_idx, col_idx] }
}
Another way:
array = [["foo", "bar", "lobster"], ["camel", "trombone", "foo"],
["goat", "car", "hog"], ["foo", "harp", "foo"]]
array.each_with_index.with_object([]) { |(a,i),b|
a.each_with_index { |s,j| b << [i,j] if s == "foo" } }
#=> [[0,0], [1,2], [3,0], [3,2]
It's better to work with flat arrays.
cycle = array.first.length
#=> 3
array.flatten.to_enum.with_index
.select{|e, i| e == "foo"}
.map{|e, i| i.divmod(cycle)}
#=> [[0, 0], [1, 2]]
or
cycle = array.first.length
#=> 3
array = array.flatten
array.each_index.select{|i| array[i] == "foo"}.map{|e, i| i.divmod(cycle)}
#=> [[0, 0], [1, 2]]
In the Ruby Array Class documentation, I often find:
If no block is given, an enumerator is returned instead.
Why would I not pass a block to #map? What would be the use of my doing just:
[1,2,3,4].map
instead of doing:
[1,2,3,4].map{ |e| e * 10 } # => [10, 20, 30, 40]
Can someone show me a very practical example of using this enumerator?
Good question.
What if we want to do multiple things with the enumerator that is created? We don't want to process it now, because it means we may need to create another later?
my_enum = %w[now is the time for all good elves to get to work].map # => #<Enumerator: ["now", "is", "the", "time", "for", "all", "good", "elves", "to", "get", "to", "work"]:map>
my_enum.each(&:upcase) # => ["NOW", "IS", "THE", "TIME", "FOR", "ALL", "GOOD", "ELVES", "TO", "GET", "TO", "WORK"]
my_enum.each(&:capitalize) # => ["Now", "Is", "The", "Time", "For", "All", "Good", "Elves", "To", "Get", "To", "Work"]
The main distinction between an Enumerator and most† other data structures in the Ruby core library (Array, Hash) and standard library (Set, SortedSet) is that an Enumerator can be infinite. You cannot have an Array of all even numbers or a stream of zeroes or all prime numbers, but you can definitely have such an Enumerator:
evens = Enumerator.new do |y|
i = -2
y << i += 2 while true
end
evens.take(10)
# => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
zeroes = [0].cycle
zeroes.take(10)
# => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
So, what can you do with such an Enumerator? Well, three things, basically.
Enumerator mixes in Enumerable. Therefore, you can use all Enumerable methods such as map, inject, all?, any?, none?, select, reject and so forth. Just be aware that an Enumerator may be infinite whereas map returns an Array, so trying to map an infinite Enumerator may create an infinitely large Array and take an infinite amount of time.
There are wrapping methods which somehow "enrich" an Enumerator and return a new Enumerator. For example, Enumerator#with_index adds a "loop counter" to the block and Enumerator#with_object adds a memo object.
You can use an Enumerator just like you would use it in other languages for external iteration by using the Enumerator#next method which will give you either the next value (and move the Enumerator forward) or raise a StopIteration exception if the Enumerator is finite and you have reached the end.
† Eg., an infinite range: (1..1.0/0)
The feature of returning a enumerable when no block is given is mostly used when chaining functions from the enumerable class together. Like this:
abc = %w[a b c]
p abc.map.with_index{|item, index| [item, index]} #=> [["a", 0], ["b", 1], ["c", 2]]
edit:
I think my own understanding of this behavior is a bit too limited in order to give a proper understanding of the inner workings of Ruby. I think the most important thing to note is that arguments are passed on in the same way they are for Procs. Thus if an array is passed in, it will be automatically 'splatted' (any better word for this?). I think the best way to get an understanding is to just use some simple functions returning enumerables and start experimenting.
abc = %w[a b c d]
p abc.each_slice(2) #<Enumerator: ["a", "b", "c", "d"]:each_slice(2)>
p abc.each_slice(2).to_a #=> [["a", "b"], ["c", "d"]]
p abc.each_slice(2).map{|x| x} #=> [["a", "b"], ["c", "d"]]
p abc.each_slice(2).map{|x,y| x+y} #=> ["ab", "cd"]
p abc.each_slice(2).map{|x,| x} #=> ["a", "c"] # rest of arguments discarded because of comma.
p abc.each_slice(2).map.with_index{|array, index| [array, index]} #=> [[["a", "b"], 0], [["c", "d"], 1]]
p abc.each_slice(2).map.with_index{|(x,y), index| [x,y, index]} #=> [["a", "b", 0], ["c", "d", 1]]
In addition to hirolau's answer, there is another method lazy that you can combine to modify the enumerator.
a_very_long_array.map.lazy
If map always took a block, then there would have to be another method like map_lazy, and similarly for other iterators to do the same thing.
I guess sometimes you want to pass the Enumerator to another method, despite where this Enumerator came from: map, slice, whatever:
def report enum
if Enumerator === enum
enum.each { |e| puts "Element: #{e}" }
else
raise "report method requires enumerator as parameter"
end
end
> report %w[one two three].map
# Element: one
# Element: two
# Element: three
> report (1..10).each_slice(2)
# Element: [1, 2]
# Element: [3, 4]
# Element: [5, 6]
# Element: [7, 8]
# Element: [9, 10]
Enumerator A class which allows both internal and external iteration
=> array = [1,2,3,4,5]
=> array.map
=> #<Enumerator: [2, 4, 6, 8, 10]:map>
=> array.map.next
=> 2
=> array.map.next_values
=> [0] 2
I'm trying to convert an Array of Arrays consisting of Ruby Strings into an Array of Arrays consisting of Strings and Floats.
Here is my attempt:
array = [["My", "2"], ["Cute"], ["Dog", "4"]]
array.collect! do |x|
x.each do |y|
if y.gsub!(/\d+/){|s|s.to_f}
end
end
end
=> [["My", "2.0"], ["Cute"], ["Dog", "4.0"]]
I'm looking for this to rather return [["My", 2.0], ["Cute"], ["Dog", 4.0]] What did I do wrong?
What you did wrong is that you used gsub!. That takes a string and changes the string. It doesn't turn it into anything else, no matter what you do (even if you convert it to a float in the middle).
A simple way to achieve what you want is:
[["My", "2"], ["Cute"], ["Dog", "4"]].map{|s1, s2| [s1, *(s2.to_f if s2)]}
If you do not want to create the element array, but replace its contents, then:
[["My", "2"], ["Cute"], ["Dog", "4"]].each{|a| a[1] = a[1].to_f if a[1]}
If the numerical strings appear in random positions, then:
[["My", "2"], ["Cute"], ["Dog", "4"]]
.each{|a| a.each.with_index{|e, i| a[i] = a[i].to_f if a[i] and a[i] =~ /\d+/}}
I was looking for a way to convert two arrays into a single hash. I found something like this :
a1 = [1,2,3]
a2 = [?A, ?B, ?C]
Hash[*a1.zip(a2).flatten]
I thought that this syntax was a bit weird, because Hash[a1.zip a2] would do exactly the same. But more than that, I don't understand the need for the * operator.
I know that it turns objects into arrays, or something alike (but not in the same way [] does, apparently).
When I execute :
a = a1.zip(a2).flatten
=> [1, "A", 2, "B", 3, "C"]
a = *a1.zip(a).flatten
=> [1, "A", 2, "B", 3, "C"]
Nothing really happens, and for what I know of the * operator, this seems to be the normal behavior.
So, why does
Hash[*a1.zip(a2).flatten]
=> {1=>"A", 2=>"B", 3=>"C"}
Hash[a1.zip(a).flatten]
=> {}
Return different values, given that the parameters seem identical ?
I guess I must be missing something about the * operator.
Thanks.
When the * operator is used with arrays like that it is called the splat operator.
Think of it as an operator that removes the first level of brackets around an array. This is quite useful because you can turn arrays into argument lists:
def stuff(x, y, z)
end
a = [1, 2, 3]
stuff(*a) # x,y,z gets assigned 1,2,3
The same thing works with Hash[]. The [] operator on Hash accepts as arguments:
An argument list of key-value pairs:
Hash["a", 1, "b", 2] #=> { "a" => 1, "b" => 2 }
An array or array pairs representing key-values:
Hash[ [["a", 1], ["b", 2]] ] #=> { "a" => 1, "b" => 2 }
Hash[] not does NOT accept a plain flat array as arguments:
Hash[ ["a", 1, "b", 2] ] #=> {}
So with this in mind, plus our understanding what the splat operator does you can now see what is happening:
paired_array = a1.zip(a2)
=> [[1, "A"], [2, "B"], [3, "C"]]
plain_array = a1.zip(a2).flatten
=> [1, "A", 2, "B", 3, "C"]
# Per rule 2 above we know this works
Hash[paired_array]
=> {1=>"A", 2=>"B", 3=>"C"}
# This won't work
Hash[plain_array]
=> {}
# But if we turn the plain_array into an argument list,
# then we know per rule 1 above that this will work
Hash[*plain_array]
=> {1=>"A", 2=>"B", 3=>"C"}
Now then you might be wondering what the hey is happening when you do:
a = *plain_array
=> [1, "A", 2, "B", 3, "C"]
Since we know the splat operator effectively strips the brackets, we get this:
a = 1, "A", 2, "B", 3, "C"
...which funnily enough is valid Ruby code and just creates an array again.
You can read more fun stuff about the splat operator in the rubyspec test case for the splat operator.
I think there's a mistake in your example, it should be like this:
Hash[a1.zip(a2).flatten] #=> {}
Hash[*a1.zip(a2).flatten] #=> {1=>"A", 2=>"B", 3=>"C"}
The splat operator in the assign mode converts an array to multiple arguments:
duck, cow, pig = *["quack","mooh","oing"] #=> ["quack","mooh","oing"]
Actually it's identical to
duck, cow, pig = ["quack","mooh","oing"] #=> ["quack","mooh","oing"]
But from the documentation you can see that Hash[...] receives multiple arguments, so the splat operator helps to assign each of those multiple arguments.
It's not that mysterious:
a1 = [1,2,3]
a2 = [?A, ?B, ?C]
p Hash[*a1.zip(a2).flatten] #{1=>"A", 2=>"B", 3=>"C"}
The * converts the array to a mere list (of arguments).
But why wasn't this syntax used?
p Hash[a1.zip(a2)]# {1=>"A", 2=>"B", 3=>"C"}
Well, it is new since Ruby 1.9.2. Your example is probably older.