Sum of duplicate values with multiple values in hash - ruby

Given a hash that looks like the following:
h = {
"0" => ["1", "true", "21"],
"1" => ["2", "true", "21"],
"2" => ["3", "false", "21"],
"3" => ["4", "true", "22"],
"4" => ["5", "true", "22"],
"5" => ["6", "true", "22"],
"6" => ["7", "false", "21"]
}
I want to find the sum of elements at position 0 across arrays that have the same elements at indices 1 and 2, and return a hash like the following:
{
0 => ["3", "true", "21"],
1 => ["10", "false", "21"],
2 => ["15", "true", "22"]
}
Since there are two arrays with indices 1 and 2 having values "true" and "21", I want to sum the integer values of index 0 for those two arrays, for example.
How can I convert the example hash at the top of this question to the resultant hash below it?

Code
def group_em(h)
h.group_by { |_,v| v.drop(1) }.
transform_values do |a|
a.transpose.
last.
map(&:first).
sum(&:to_i).
to_s
end.
each_with_index.
with_object({}) { |((a,v),i),g| g[i] = [v,*a] }
end
Example
h = {
"0" => ["1", "true", "21"],
"1" => ["2", "true", "21"],
"2" => ["3", "false", "21"],
"3" => ["4", "true", "22"],
"4" => ["5", "true", "22"],
"5" => ["6", "true", "22"],
"6" => ["7", "false", "21"]
}
group_em(h)
#=> {0=>["3", "true", "21"],
# 1=>["10", "false", "21"],
# 2=>["15", "true", "22"]}
Explanation
The major steps
For the hash h above the major steps are as follows.
p = h.group_by { |_,v| v.drop(1) }
#=> {["true", "21"]=>[["0", ["1", "true", "21"]],
# ["1", ["2", "true", "21"]]],
# ["false", "21"]=>[["2", ["3", "false", "21"]],
# ["6", ["7", "false", "21"]]],
# ["true", "22"]=>[["3", ["4", "true", "22"]],
# ["4", ["5", "true", "22"]],
# ["5", ["6", "true", "22"]]]}
q = p.transform_values do |a|
a.transpose.
last.
map(&:first).
sum(&:to_i).
to_s
end
#=> {["true", "21"]=>"3", ["false", "21"]=>"10", ["true", "22"]=>"15"}
enum0 = q.each_with_index
#=> #<Enumerator: {["true", "21"]=>"3", ["false", "21"]=>"10",
# ["true", "22"]=>"15"}:each_with_index>
enum1 = enum0.with_object({})
#=> #<Enumerator: #<Enumerator: {["true", "21"]=>"3", ["false", "21"]=>"10",
# ["true", "22"]=>"15"}:each_with_index>:with_object({})>
enum1.each { |((a,v),i),g| g[i] = [v,*a] }
#=> {0=>["3", "true", "21"],
# 1=>["10", "false", "21"],
# 2=>["15", "true", "22"]}
We can see the values that will be generated and passed to the block by by the enumerator enum1 by converting it to an array:
enum1.to_a
#=> [[[[["true", "21"], "3"], 0], []],
# [[[["false", "21"], "10"], 1], []],
# [[[["true", "22"], "15"], 2], []]]
If you compare the return value for enum0 with that of enum1 you can think of the latter as a compound enumerator, though Ruby does not employ that term.
Details of Hash#transform_values
Now let's look more closely at the calculation of q. The first value of p is passed to the block by Hash#transform_values (which made its debut in MRI 2.4) and becomes the value of the block variable a:
a = p.first.last
#=> [["0", ["1", "true", "21"]], ["1", ["2", "true", "21"]]]
The block calculations are as follows.
b = a.transpose
#=> [["0", "1"], [["1", "true", "21"], ["2", "true", "21"]]]
c = b.last
#=> [["1", "true", "21"], ["2", "true", "21"]]
d = c.map(&:first) # ~same as c.map { |a| a.first }
#=> ["1", "2"]
e = e.sum(&:to_i) # ~same as e.sum { |s| s.to_i }
#=> 3
e.to_s
#=> "3"
We see that the value a has been transformed to "3". The remaining calculations to compute q are similar.
Documentation links
You can find documentation for the methods I've used at the following links for classes Array (drop, transpose, last, first and sum), Integer (to_s), String (to_i) and Enumerator (with_object and next), and the module Enumerable (group_by, map and each_with_index).
Decomposition of nested objects
There is one more tricky-bit I would like to mention. That is the line
enum1.each { |((a,v),i),g| g[i] = [v,*a] }
I've written the block variables in such a way to decompose the values that are generated by the enumerator enum1 and passed to the block. I'm sure that it must look quite imposing for a newbie, but it's not so bad if you take step-by-step, as I will explain.
Firstly, suppose I had a single block variable r (enum1.each { |r|...}). The first value is generated and passed to the block, assigning a value to r:
r = enum1.next
#=> [[[["true", "21"], "3"], 0], []]
We could then execute the following statement in the block to decompose (of disambiguate) r as follows:
((a,v),i),g = r
#=> [[[["true", "21"], "3"], 0], []]
producing the following assignments:
a #=> ["true", "21"]
v #=> "3"
i #=> 0
g #=> []
It is equivalent, and simpler, to replace |r| in the block with |((a,v),i),g|.
If you study the locations of the brackets in the nested array produced by enum1.next you will see how I determined where I needed parentheses when writing the block variables. This decomposition of nested arrays and other objects is a very convenient and powerful feature or Ruby, one that is much underused.

I am not a ruby developer so I can't suggest any best practices but simple algorithm that comes in my mind after reading this, is to create a new hash and check if array values are in it or not, if not then append new value like this.
h = {
"0" => ["1", "true", "21"],
"1" => ["2", "true", "21"],
"2" => ["3", "false", "21"],
"3" => ["4", "true", "22"],
"4" => ["5", "true", "22"],
"5" => ["6", "true", "22"],
"6" => ["7", "false", "21"]
}
new_h = {}
h.each do |key, val|
x1 = val.at(1)
x2 = val.at(2)
found = false
new_h.each do |key1, val2|
y1 = val2.at(1)
y2 = val2.at(2)
if x1 === y1 && x2 === y2
found = true
arr = [val2.at(0).to_i + val.at(0).to_i, x1, x2]
new_h[key1] = arr
end
end
if !found
new_h[new_h.length] = val
end
if new_h.empty?
new_h[key] = val
end
end
puts "#{new_h}"

Just out of curiosity.
input.
values.
map { |i, *rest| [rest, i.to_i] }.
group_by(&:shift).
map do |*key, values|
[values.flatten.sum.to_s, *key.flatten]
end

references : Enumerable#group_by, Enumerator#with_index, Array#to_h
key_sum = ->(group) { group.sum { |key, _| key.to_i }.to_s }
given_hash.values.group_by { |_, *rest| rest }.
map.with_index { |(key, group), idx| [idx, [key_sum.call(group), *key]] }.to_h
#=> {0=>["3", "true", "21"], 1=>["10", "false", "21"], 2=>["15", "true", "22"]}
group by
given_hash.values.group_by { |_, *rest| rest }
#=> { ["true", "21"] => [["1", "true", "21"], ["2", "true", "21"]]...
key_sum function
key_sum = ->(group) { group.sum { |key, _| key.to_i }.to_s }
key_sum.call([["1", "true", "21"], ["2", "true", "21"]]) #=> '3'
to_h
[[0, ["3", "true", "21"]], [1, ["10", "false", "21"]], [2, ["15", "true", "22"]]].to_h
#=> {0=>["3", "true", "21"], 1=>["10", "false", "21"], 2=>["15", "true", "22"]}

This should answer your question, despite being a very long way of solving it. I'm sure there are shortcuts to solve it more easily but you requested a clear explanation of what's happening and I hope that this guides you through how to solve it.
# Start with a hash
hash = {
"0" => ["1", "true", "21"],
"1" => ["2", "true", "21"],
"2" => ["3", "false", "21"],
"3" => ["4", "true", "22"],
"4" => ["5", "true", "22"],
"5" => ["6", "true", "22"],
"6" => ["7", "false", "21"]
}
# Extract just the values from the hash into an array
values = hash.values
added_values = values.map do |array|
# Find all arrays that match this array's values at indices [1] and [2]
matching = values.select { |a| a[1] == array[1] && a[2] == array[2] }
sum = 0
# Add the values at index 0 from each matching array
matching.each { |match| sum += match[0].to_i }
# Return a new array with these values
[sum.to_s, array[1], array[2]]
end
# Reject any duplicative arrays
added_values.uniq!
# Convert the array back to a hash
added_values.each_with_index.each_with_object({}) { |(array, index), hash| hash[index] = array }

Hash and Array have most powerful in-built functions in ruby.
z = h.group_by { |k,v| v[1..2] }.keep_if { |k,v| v.length > 1 }
val = z.map { |k,v| [v.map { |x| x[1] }.map(&:first).map(&:to_i).inject(:+).to_s, k[0], k[1]] }
val.each_with_index.inject({}) { |m,(x,i)| m[i] = x; m }
=> {0 =>["3", "true", "21"], 1 =>["10", "false", "21"], 2 =>["15", "true", "22"]}
If you know these functions then you do not need complex implementation ever. Happy learning :)

h
.values
.group_by{|_, *a| a}
.map
.with_index{|(k, a), i| [i, [a.inject(0){|acc, (n, *)| acc + n.to_i}.to_s, *k]]}
.to_h
# => {0=>["3", "true", "21"], 1=>["10", "false", "21"], 2=>["15", "true", "22"]}

Related

How to split a long number into pairs of digits

I have a string of a long number 12345678 and want to convert it to an array like this :
["12", "34", "56", "78"].
I have tried array.split(//).map { |e| e.to_i } but it does
["1", "2", "3", "4", "5", "6", "7", "8"]
The simplest way would be to use String#scan with the Regexp /../, which matches any pair of characters:
n = 12345678
arr = n.to_s.scan(/../)
# => ["12", "34", "56", "78"]
If you need to handle odd numbers of digits and keep the last digit, use /..?/ instead:
n = 123456789
arr = n.to_s.scan(/..?/)
# => ["12", "34", "56", "78", "9"]
An alternative approach would be to map over the range 0...n.to_s.size using the Range#step method:
n = 123456789
str = n.to_s
arr = (0...str.size).step(2).map {|i| str[i, 2] }
# => ["12", "34", "56", "78", "9"]
You can see all three approaches in action on repl.it: https://repl.it/#jrunning/BlissfulAcclaimedStrategy
I think there is no need to use a regexp, I would do something like this:
12345678.to_s.chars.each_slice(2).map(&:join)
#=> ["12","34","56","78"]
You can use
p = 123456789.to_s
(0..p.length).each_cons(2).map {|i, j| "#{p[i]}#{p[j]}" if i%2 == 0}.compact

How to detect duplicate keys in hash and add prefix to the duplicate?

I have two arrays and I am creating a key-value-pair using hash in Ruby. How can I detect a duplicate key when zipping two arrays into key-value-pair and adding a prefix like "A-" in front of the key name for the duplicates?
I am using .zip to merge two arrays and making one a key and other one a value
[0] = "David"
[1] = "John"
[2] = "Alex"
[3] = "Sam"
[4] = "Caleb"
[5] = "David"
[6] = "John"
[7] = "Alex"
[8] = "Sam"
[0] = "1"
[1] = "2"
[2] = "3"
[3] = "4"
[4] = "5"
[5] = "6"
[6] = "7"
[7] = "8"
[8] = "9"
name_number_key_value_pair_hash = first_names.zip(numbers).to_h
puts(name_number_key_value_pair_hash)
Expected:
{"David"=>"1", "John"=>"2", "Alex"=>"3", "Sam"=>"4", "Caleb"=>"5", "A-David"=>"6", "A-John"=>"7", "A-Alex"=>"8", "A-Sam"=>"9"}
Actual:
{"David"=>"6", "John"=>"7", "Alex"=>"8", "Sam"=>"9", "Caleb"=>"5"}
It seems straight forward Have attached code snippet
names = %w[David John Alex Sam Caleb David John Alex Sam]
numbers = %w[1 2 3 4 5 6 7 8 9]
key_pair = {}
names.each_with_index do |name, index|
name = "A-#{name}" if key_pair[name]
key_pair[name] = numbers[index]
end
It generates the expected output:
{"David"=>"1", "John"=>"2", "Alex"=>"3", "Sam"=>"4", "Caleb"=>"5", "A-David"=>"6", "A-John"=>"7", "A-Alex"=>"8", "A-Sam"=>"9"}
You basically just need to keep track of the state of the hash as you build it and, when you find a conflict, create a new key instead. This captures the general approach:
def hash_with_prefixes(a, b, prefixes)
kv_pairs = a.zip(b)
prefixes = prefixes.to_enum
result_hash = {}
kv_pairs.each do |initial_key, value|
final_key = initial_key
while result_hash.include? final_key
final_key = "#{pfx.next}-#{initial_key}"
end
prefixes.rewind
result_hash[final_key] = value
end
result_hash
rescue StopIteration
fail "Insufficient prefixes to provide unique keys for input lists."
end
At the slight expense of clarity, you can also write it in a rather shorter form:
def hash_with_prefixes(a, b, prefixes)
pi = Hash[a.map {|k| [k, prefixes.lazy.map {|p| "#{p}-#{k}"}]}]
a.zip(b).inject({}) {|h, kv| h[h.include?(kv[0]) ? pi[kv[0]].next : kv[0]] = kv[1]; h}
rescue StopIteration
fail "Insufficient prefixes to provide unique keys for input lists."
end
(Don't do this.)
This is really very simple.
names = ["John","John", "John", "David", "David", "Susan", "Sue"]
numbers = ["1", "2", "3", "4", "5", "6","7"]
def uniq_hash_keys(names, numbers)
hash = {}
names.each_with_index do |name,i|
if hash[name]
prefix = 'A1-'
key = prefix + name
while hash[key]
version = prefix.match(/A(\d+)-.*/i)[1].to_i
prefix = "A#{version + 1}-"
key = prefix + name
end
name = key
end
hash[name] = numbers[i]
end
hash
end
This function produces:
{
"John"=>"1",
"A1-John"=>"2",
"A2-John"=>"3",
"David"=>"4",
"A1-David"=>"5",
"Susan"=>"6",
"Sue"=>"7"
}
Notice that there are 3 Johns, this is why the while loop is inside the function.
This is one way to create the desired hash. Note that in arr1 "John" appears three times.
arr1 = ["David", "John", "Alex", "Sam", "Caleb",
"David", "John", "Alex", "John", "Sam"]
arr2 = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
prefixes =
arr1.each_with_object({}) do |s,h|
if h.key?(s)
prefix = "A-"
(h[s].size-1).times { prefix = prefix.next }
h[s] << prefix
else
h[s] = ['']
end
end
#=> {"David"=>["", "A-"], "John"=>["", "A-", "B-"],
# "Alex"=>["", "A-"], "Sam"=>["", "A-"],
# "Caleb"=>[""]}
arr1.map { |s| "#{prefixes[s].shift}#{s}" }.zip(arr2).to_h
#=> {"David"=>"1", "John"=>"2", "Alex"=>"3", "Sam"=>"4",
# "Caleb"=>"5", "A-David"=>"6", "A-John"=>"7",
# "A-Alex"=>"8", "B-John"=>"9", "A-Sam"=>"10"}
Note that "A-".next #=> "B-" and "Z-".next #=> "AA-".
Alternative data structure
You may wish to consider a different data structure, one that returns
{"David"=>["1", "6"], "John"=>["2", "7", "9"],
"Alex" =>["3", "8"], "Sam" =>["4", "10"], "Caleb"=>["5"]}
You could do that as follows.
arr1.each_with_index.
group_by(&:first).
transform_values { |v| arr2.values_at(*v.map(&:last)) }
#=> {"David"=>["1", "6"], "John"=>["2", "7", "9"],
# "Alex" =>["3", "8"], "Sam" =>["4", "10"],
# "Caleb"=>["5"]}
See Enumerable#each_with_index, Enumerable#group_by, Hash#transform_values1 and Array#values_at. v.map(*:last) is here the same as v.map { |arr| arr.last }.
The steps are as follows.
a = arr1.each_with_index
#=> #<Enumerator: ["David", "John", "Alex", "Sam",
# "Caleb", "David", "John", "Alex", "John", "Sam"]:
# each_with_index>
We can see the values that will be generated by this enumerator by converting it to an array.
a.to_a
#=> [["David", 0], ["John", 1], ["Alex", 2], ["Sam", 3],
# ["Caleb", 4], ["David", 5], ["John", 6], ["Alex", 7],
# ["John", 8], ["Sam", 9]]
Continuing,
b = a.group_by(&:first)
#=> {"David"=>[["David", 0], ["David", 5]],
# "John"=> [["John", 1], ["John", 6], ["John", 8]],
# "Alex"=> [["Alex", 2], ["Alex", 7]],
# "Sam"=> [["Sam", 3], ["Sam", 9]],
# "Caleb"=>[["Caleb", 4]]}
b.transform_values { |v| arr2.values_at(*v.map(&:last)) }
#=> {"David"=>["1", "6"], "John"=>["2", "7", "9"],
# "Alex"=> ["3", "8"], "Sam"=> ["4", "10"], "Caleb"=>["5"]}
For the last step, the first value of the hash b is passed to the block and the block variable is assigned to that value.
v = b.values.first
#=> [["David", 0], ["David", 5]]
The block calculations are then as follows.
c = v.map(&:last)
#=> [0, 5]
arr2.values_at(*c)
#=> arr2.values_at(0, 5)
#=> ["1", "6"]
The calculations are similar for each of the remaining values of b that are passed to the block.
1. New in Ruby MRI v2.4.
This code is less readable but compact and functional-style.
It conceptually the same as rahul mishra code https://stackoverflow.com/a/54697573/2109121
names = %w[David John Alex Sam Caleb David John Alex Sam]
numbers = %w[1 2 3 4 5 6 7 8 9]
result = names.zip(numbers).reduce({}) { |a, (b, c)| a.merge(a.key?(b) ? "A-#{b}" : b => c) }
Using zip and each_with_object
names = %w[David John Alex Sam Caleb David John Alex Sam]
numbers = %w[1 2 3 4 5 6 7 8 9]
names.zip(numbers).each_with_object({}) do |(name, number), hash|
key = hash.key?(name) ? "A-#{name}" : name
hash[key] = number
end

Converting an array into a hash with format { value => position_in_array }

I have an array of numbers in string format, and I want to convert them into a hash where the keys are the numbers and the values are the positions of those numbers in the array. So for example:
["1", "5", "3"]
should result in:
{ 1 => 0, 5 => 1, 3 => 2 }
I have the following code, which works:
my_hash = {}
my_array.each do |number_string|
my_hash[number_string.to_i] = my_array.index(number_string)
end
which iterates through the array and pushes each value and its position into the hash.
Is there a shorter and more elegant way to do it? Maybe something similar to Ruby's to_a function, but more like to_h(options).
Hash[["1", "5", "3"]
.map.with_index{|e, i| [e.to_i, i]}]
# => {1=>0, 5=>1, 3=>2}
or
["1", "5", "3"]
.each_with_object({}).with_index{|(e, h), i| h[e.to_i] = i}
# => {1=>0, 5=>1, 3=>2}
arr = ["1", "5", "3"]
ha = Hash[arr.map.with_index {|a, i| [a.to_i, i]}]
puts "ha: #{ha.inspect}"
irb(main):038:0> arr=["1", "5", "3"]
=> ["1", "5", "3"]
irb(main):039:0> Hash[arr.map.with_index {|a, i| [a, i]}]
=> {"1"=>0, "5"=>1, "3"=>2}
irb(main):040:0> Hash[arr.map.with_index {|a, i| [a.to_i, i]}]
=> {1=>0, 5=>1, 3=>2}

Using each_slice to split input starting from end of string?

I would like to split a string into array groups of three as shown in desired output. Using Array#each_slice like this 1_223_213_213.to_s.split('').each_slice(3){|arr| p arr }
Current output: Desired output
# ["1", "2", "2"] # ["0", "0", "1"]
# ["3", "2", "1"] # ["2", "2", "3"]
# ["3", "2", "1"] # ["2", "1", "3"]
# ["3"] # ["2", "1", "3"]
Must work with numbers from (0..trillion). I posted my solution as an answer below. Hoping you all can give me some suggestion(s) to optimize or alternative implements?
Try left-padding with zeros until the string length is an even multiple of your "slice" target:
def slice_with_padding(s, n=3, &block)
s = "0#{s}" while s.to_s.size % n != 0
s.to_s.chars.each_slice(n, &block)
end
slice_with_padding(1_223_213_213) { |x| puts x.inspect }
# ["0", "0", "1"]
# ["2", "2", "3"]
# ["2", "1", "3"]
# ["2", "1", "3"]
slice_with_padding(12_345, 4) { |x| puts x.inspect }
# ["0", "0", "0", "1"]
# ["2", "3", "4", "5"]
You might find this a little more pleasing to your eye:
def slice_by_3(n)
n = n.to_s
l = n.length
[*n.rjust(l % 3 == 0 ? l : l + 3 - l % 3, '0').chars.each_slice(3)]
end
slice_by_3 2_123_456_544_545_355
=> [["0", "0", "2"],
["1", "2", "3"],
["4", "5", "6"],
["5", "4", "4"],
["5", "4", "5"],
["3", "5", "5"]]
Alternatively, if you want a more general solution:
def slice_by_n(num, n=3)
num = num.to_s
l = num.length
[*num.rjust(l % n == 0 ? l : l + n - l % n, '0').chars.each_slice(n)]
end
Here is a possible solution for the problem:
def slice_by_3 number
initial_number = number.to_s.split('').size
number = "00#{number}" if initial_number == 1
modulus = number.to_s.split(/.{3}/).size
padleft = '0' * ( (modulus*3) % number.to_s.split('').size )
("#{padleft}#{number}").split('').each_slice(3){|arr| p arr }
end
slice_by_3 2_123_456_544_545_355
# ["0", "0", "2"]
# ["1", "2", "3"]
# ["4", "5", "6"]
# ["5", "4", "4"]
# ["5", "4", "5"]
# ["3", "5", "5"]
Just seems somewhat complex and I want to believe there is a better way. I appreciate your feedback.
def slice_by_3 number
"000#{number}".split('').reverse
.each_slice(3).to_a[0..-2].reverse
.each { |arr| p arr.reverse }
end
slice_by_3 13_456_544_545_355
# ["0", "1", "3"]
# ["4", "5", "6"]
# ["5", "4", "4"]
# ["5", "4", "5"]
# ["3", "5", "5"]
This code reverses the whole array after adding 3 zeroes to the number start. each_slice(3) then slices to the proper groups (although reversed) plus one which consists of either ["0","0","0"], ["0","0"] or ["0"] depending on the original length of the number.
[0..-2] cuts the last group of zeroes. Then the groups are reversed back, and each group is printed (reversed back).
Here are a couple methods
n = 1_223_213_213.to_s
n.rjust(n.size + n.size % 3,"0").chars.each_slice(3).to_a
OR
n.rjust(15,"0").chars.each_slice(3).drop_while{|a| a.join == "000"}
15 is because you stated the max was a trillion obviously this number means very little as it rejects all results that contain all zeros so any number greater than 15 that is divisible by 3 will work for your example
Another way:
def split_nbr(n)
str = n.to_s
len = str.size
str.rjust(len + (3-len%3)%3, '0').scan(/.../).map(&:chars)
end
split_nbr( 1_223_213_213)
#=> [["0", "0", "1"], ["2", "2", "3"], ["2", "1", "3"], ["2", "1", "3"]]
split_nbr( 11_223_213_213)
#=> [["0", "1", "1"], ["2", "2", "3"], ["2", "1", "3"], ["2", "1", "3"]]
split_nbr(111_223_213_213)
#=> [["1", "1", "1"], ["2", "2", "3"], ["2", "1", "3"], ["2", "1", "3"]]

To find the integer (Fixnum) values in ruby array

I have an array [1, 2, "3", "4", "1a", "abc", "a"] with
pure integers (1, 2),
string formatted integers ("1", "2"),
strings ("a", "b"), and
mixed string numbers ("1a", "2s").
From this, I need to pick up only the integers (including string formatted) 1, 2, "3", "4".
First I tried with to_i:
arr = [1, 2, "3", "4", "1a", "abc", "a"]
arr.map {|x| x.to_i}
# => [1, 2, 3, 4, 1, 0, 0]
but this one converts "1a" to 1, which I don't expect.
Then I tried Integer(item):
arr.map {|x| Integer(x) } # and it turned out to be
# => ArgumentError: invalid value for Integer(): "1a"
Now I am out of straight conversion options here. Finally, I decided to do this way, which converts the value to_i and to_s. So "1" == "1".to_i.to_s is an integer, but not "1a" == "1a".to_i.to_s and "a" == "a".to_i.to_s
arr = arr.map do |x|
if (x == x.to_i.to_s)
x.to_i
else
x
end
end
and
ids, names= arr.partition { |item| item.kind_of? Fixnum }
Now I got the arrays of integers and strings. Is there a simple way to do this?
Similar solution as provided by #maerics, but a bit slimmer:
arr.map {|x| Integer(x) rescue nil }.compact
class Array
def to_i
self.map {|x| begin; Integer(x); rescue; nil; end}.compact
end
end
arr = [1, 2, "3", "4", "1a", "abc", "a"]
arr.to_i # => [1, 2, 3, 4]
something like this:
a = [1,2,"3","4","1a","abc","a"]
irb(main):005:0> a.find_all { |e| e.to_s =~ /^\d+$/ }.map(&:to_i)
=> [1, 2, 3, 4]
Hey, thanks awakening my ruby. Here is my go at this problem:
arr=[1,2,"3","4","1a","abc","a"]
arr.map {|i| i.to_s}.select {|s| s =~ /^[0-9]+$/}.map {|i| i.to_i}
//=> [1, 2, 3, 4]
I noticed most of the answer so far changes the value of "3" and "4" to actual integers.
>> array=[1, 2, "3", "4", "1a", "abc", "a", "a13344a" , 10001, 3321]
=> [1, 2, "3", "4", "1a", "abc", "a", "a13344a", 10001, 3321]
>> array.reject{|x| x.to_s[/[^0-9]/] }
=> [1, 2, "3", "4", 10001, 3321]
#OP, I have not tested my solution exhaustively, but so far it seems to work (of course its done according to provided sample ), so please test thoroughly yourself.
How about this?
[1,2,"3","4","1a","abc","a"].select{|x| x.to_i.to_s == x.to_s}
# => [1, 2, "3", "4"]
Looks pretty simple
arr.select{ |b| b.to_s =~ /\d+$/ }
# or
arr.select{ |b| b.to_s[/\d+$/] }
#=> [1, 2, "3", "4"]

Resources