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+/}}
Related
Is there any builtin method to produce combinations of consecutive array elements?
a = ['1','2','3','4']
# => '12','23','34'
I tried the methods permutation, combination, and each_slice, but was not able to produce required output.
a.permutation(2).to_a #=> [[1,2],[1,3],[1,4],[2,1],[2,3],[2,4],[3,1],[3,2],[3,4]]
a.combination(2).to_a #=> [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
a.each_slice(2) {|a| p a} #=> ["1", "2"],["3", "4"]
No, but you can do it with a combination of a few methods.
a.each_cons(2).map(&:join)
# => ["12", "23", "34"]
I have the following:
lumpy_hash = { 1 => ["A", "B"] }
then if I invoke Hash#invert on this hash, I'd like to get:
lumpy_hash = {"A" => 1, "B" => 1}
I don't get that from using Hash#invert. Any ideas on doing this? I'm not sure if I should try Hash#map or Hash#invert.
There are many ways to do this. Here is one:
Hash[lumpy_hash.map { |k,v| v.product([k]) }.first]
#=> {"A"=>1, "B"=>1}
I don't think the method Hash#invert is useful here.
The steps:
enum = lumpy_hash.map
#=> #<Enumerator: {1=>["A", "B"]}:map>
k,v = enum.next
#=> [1, ["A", "B"]]
k #=> 1
v #=> ["A", "B"]
a = v.product([k])
#=> ["A", "B"].product([1])
#=> [["A", 1], ["B", 1]]
Hash[a]
#=> {"A"=>1, "B"=>1}
Here's another way that makes use of a hash's default value. This one is rather interesting:
key,value = lumpy_hash.to_a.first
#=> [1, ["A","B"]]
Hash.new { |h,k| h[k]=key }.tap { |h| h.values_at(*value) }
#=> {"A"=>1,"B"=>1}
Object#tap passes an empty hash to its block, assigning it to the block variable h. The block returns h after adding three key-value pairs, each having a value equal to the hash's default value. It adds the pairs merely by computing the values of keys the hash doesn't have!
Here's another, more pedestrian, method:
lumpy_hash.flat_map{|k,vs| vs.map{|v| {v => k}}}.reduce(&:merge)
=> {"A"=>1, "B"=>1}
wordfrequency = Hash.new(0)
splitfed.each { |word| wordfrequency[word] += 1 }
wordfrequency = wordfrequency.sort_by {|x,y| y }
wordfrequency.reverse!
puts wordfrequency
I have added the words into a hash table and have gotten it to sort by word frequency, but then order within each frequency is random when I want it to be in alphabetical order. Any quick fixes? Thanks! Much appreciated.
You can use:
wordfrequency = wordfrequency.sort_by{|x,y| [y, x] }
to sort by the value then the key.
In your case,
splitfed = ["bye", "hi", "hi", "a", "a", "there", "alphabet"]
wordfrequency = Hash.new(0)
splitfed.each { |word| wordfrequency[word] += 1 }
wordfrequency = wordfrequency.sort_by{|x,y| [y, x] }
wordfrequency.reverse!
puts wordfrequency.inspect
will output:
[["hi", 2], ["a", 2], ["there", 1], ["bye", 1], ["alphabet", 1]]
which is reverse ordered by the occurrence of the word then the word itself.
Make sure you note (which might be pretty obvious) that wordfrequency is now an array.
Hashes do not necessarily sort in natural order; it is down to the individual data structure. If you want to pretty print a hash, you need to sort the keys, then iterate over that sorted list of keys, outputting the value for each key as you go.
There are tricks you can do to do this on a single line, or collect the entries from the hash into a sorted array of arrays, but ultimately they all come back to sorting the keys then retrieving the data for the sorted key list.
Some hashes maintain insertion order, some hashes maintain a sorted structure which you can then traverse as you process the hash, but these are exceptions to the rule.
Ruby's group_by is the basis for this:
words = %w[foo bar bar baz]
words.group_by{ |w| w }
# => {"foo"=>["foo"], "bar"=>["bar", "bar"], "baz"=>["baz"]}
words.group_by{ |w| w }.map{ |k, v| [k, v.size ] }
# => [["foo", 1], ["bar", 2], ["baz", 1]]
If you want to sort by the words then by their frequency:
words.group_by{ |w| w }.map{ |k, v| [k, v.size ] }.sort_by{ |k, v| [k, v] }
# => [["bar", 2], ["baz", 1], ["foo", 1]]
If you want to sort by the frequency then by the words:
words.group_by{ |w| w }.map{ |k, v| [k, v.size ] }.sort_by{ |k, v| [v, k] }
# => [["baz", 1], ["foo", 1], ["bar", 2]]
I have a hash:
hash = {"a" => 1, "b" =>2, "c" => 3, "d" => 4}
And I have an array:
array = ["b", "a", "d"]
I would like to create a new array that is made up of the original hash values that correspond with original hash keys that are also found in the original array while maintaining the sequence of the original array. The desired array being:
desired_array = [2, 1, 3]
The idea here is to take the word "bad", assign numbers to the alphabet, and then make an array of the numbers that correspond with "b" "a" and "d" in that order.
Since your question is a little unclear I'm assuming you want desired_array to be an array (you say you want a new array and finish the sentence off with new hash). Also in your example I'm assuming you want desired_array to be [2, 1, 4] for ['b', 'a', 'd'] and not [2, 1, 3] for ['b', 'a', 'c'].
You should just you the Enumerable#map method to create a array that will map the first array to the your desired array like so:
desired_array = array.map { |k| hash[k] }
You should familiarize yourself with the Enumerable#map method, it's quite the handy method. From the rubydocs for the method: Returns a new array with the results of running block once for every element in enum. So in this case we are iterating through array and invoking hash[k] to select the value from the hash and creating a new array with values selected by the hash. Since iteration is in order, you will maintain the original sequence.
I would use Enumerable#map followed by Enumerable#sort_by, for example:
hash = {"d" => 4, "b" =>2, "c" => 3, "a" => 1}
order = ["b", "a", "d"]
# For each key in order, create a [key, value] pair from the hash.
# (Doing it this way instead of filtering the hash.to_a is O(n) vs O(n^2) without
# an additional hash-probe mapping. It also feels more natural.)
selected_pairs = order.map {|v| [v, hash[v]]}
# For each pair create a surrogate ordering based on the `order`-index
# (The surrogate value is only computed once, not each sort-compare step.
# This is, however, an O(n^2) operation on-top of the sort.)
sorted = selected_pairs.sort_by {|p| order.find_index(p[0]) }
p sorted
# sorted =>
# [["b", 2], ["a", 1], ["d", 4]]
I've not turned the result back into a Hash, because I am of the belief that hashes should not be treated as having any sort of order, except for debugging aids. (Do keep in mind that Ruby 2 hashes are ordered-by-insertion.)
All you need is values_at:
hash.values_at *array
Enumerable methods map, each works perfect
desired_array = array.map { |k| hash[k] }
or
desired_array = array.each { |k| hash[k] }
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]