Concatenating existing values within the same array in Ruby - arrays

Here's an example of an array I'm working with:
arr = [ "id1", "name1", "id2", "name2", "id3", "name3" ]
I want to change it into a new array that looks like:
new_arr = [ "id1: name1", "id2: name2", "id3: name3" ]
My attempt:
ids = arr.select.with_index { |_, i| i.even? }
names = arr.select.with_index { |_, i| i.odd? }
new_arr = ids.map { |i| i + ": " + names[ids.index(i)] }
Is there a better or more expressive way of doing this (potentially in a one-liner)?

I would use each_slice and string interpolation.
arr.each_slice(2).map { |(a, b)| "#{a}: #{b}" }
#=> ["id1: name1", "id2: name2", "id3: name3"]
Richard Hamilton`s comment made me think about the performance of the different solutions:
require 'benchmark'
arr = [ "id1", "name1", "id2", "name2", "id3", "name3" ]
slices = arr.each_slice(2)
n = 1_000_000
Benchmark.bmbm(15) do |x|
x.report("hashified :") { n.times do; Hash[*arr].map { |e| e.join ': ' } ; end }
x.report("concatenation :") { n.times do; slices.map { |a| a[0] + ": " + a[1] } ; end }
x.report("array join :") { n.times do; slices.map { |a| a.join(': ') } ; end }
x.report("interpolated :") { n.times do; slices.map { |(a, b)| "#{a}: #{b}" } ; end }
end
# Rehearsal ---------------------------------------------------
# hashified : 3.520000 0.030000 3.550000 ( 3.561024)
# concatenation : 2.300000 0.010000 2.310000 ( 2.315952)
# array join : 3.020000 0.010000 3.030000 ( 3.032235)
# interpolated : 1.950000 0.000000 1.950000 ( 1.954937)
# ----------------------------------------- total: 10.840000sec
#
# user system total real
# hashified : 3.430000 0.040000 3.470000 ( 3.473274)
# concatenation : 2.320000 0.010000 2.330000 ( 2.332920)
# array join : 3.070000 0.010000 3.080000 ( 3.081937)
# interpolated : 1.950000 0.000000 1.950000 ( 1.956998)

You can use Enumerable's each_slice method to get an enumeration of 2-element arrays from your arr. You can then simply join these elements:
arr.each_slice(2).map{|a| a.join(': ')}
What happens here is the each_slice returns an Enumerator which yields the 2-element arrays. Since Enumerators also are Enumerable, you can just use map to change these 2-element arrays and join them to a String.

each_slice is silly :)
Hash[ "id1", "name1", "id2", "name2", "id3", "name3" ].
map { |e| e.join ': ' }
#⇒ [ "id1: name1", "id2: name2", "id3: name3" ]

Try using each_slice:
arr.each_slice(2).entries.map { |ar| ar.join(': ') }
#=> ["id1: name1", "id2: name2", "id3: name3"]

You should use each_slice for this
arr.each_slice(2).map { |a| a[0] + ": " + a[1] }
=> ["id1: name1", "id2: name2", "id3: name3"]

Related

How to convert array like this ["John,Doe,11222019", "Mark,King,11232019", "Angle,Darma,11242019"] to Array of hash Ruby

How to convert array:
["John,Doe,11222019", "Mark,King,11232019", "Angle,Darma,11242019"]
to Array of hash like this using Ruby :
[
{ :name => "John Doe", :number => 11222019 },
{ :name => "Mark King", :number => 11232019 },
{ :name => "Angle Darma", :number => 11242019 },
]
Thank you very much!
You can do it simply as below,
array.map { |x| [:name, :number].zip(x.sub(',', ' ').split(',')).to_h }
# => [{:name=>"John Doe", :number=>11222019}, {:name=>"Mark King", :number=>11232019}, {:name=>"Angle Darma", :number=>11242019}]
Option using Ruby 2.6.1 Object#then:
ary = ["John,Doe,11222019", "Mark,King,11232019", "Angle,Darma,11242019"]
ary.map { |s| s.split(',').then{ |a| {name: a.first(2).join(' ') , number: a.last.to_i } } }
For Ruby 2.5.2 Object#yield_self:
ary.map { |s| s.split(',').yield_self{ |a| {name: a.first(2).join(' ') , number: a.last.to_i } } }
Both returning:
#=> [{:name=>"John Doe", :number=>11222019}, {:name=>"Mark King", :number=>11232019}, {:name=>"Angle Darma", :number=>11242019}]
arr = ["John,Doe,11222019", "Mark,King,11232019",
"Angle,Darma,11242019"]
arr.map do |s|
{name: s[/.+(?=,)/].tr(',',' '), number: s[/\d+/].to_i}
end
#=> [{:name=>"John Doe", :number=>11222019},
# {:name=>"Mark King", :number=>11232019},
# {:name=>"Angle Darma", :number=>11242019}]
The steps are as follows. Enumerable#map passes the first element of arr to the block and the block variable s is set equal to its value:
s = arr.first
#=> "John,Doe,11222019"
The block calculations are then performed:
a = s[/.+(?=,)/]
#=> "John,Doe"
This uses the method String#[] with the regular expression /.+(?=,)/. (?=,) is a positive lookahead that requires the match to be followed immediately by a comma. Because matches are by default greedy the lookahead matches the last comma in s.
b = a.tr(',',' ')
#=> "John Doe"
This uses the method String#tr. Alternatively, a.sub(',',' ') could be used.
c = s[/\d+/]
#=> "11222019"
d = c.to_i
#=> 11222019
The block then returns:
{ name: c, number: d }
#=> {:name=>"11222019", :number=>11222019}
which is the object to which s is mapped. The remaining two values of arr are passed to the block and similar calculations are performed.
a = ["John,Doe,11222019", "Mark,King,11232019", "Angle,Darma,11242019"]
Something like this
a.map do |f|
f = f.split(',')
{ name: "#{f[0]} #{f[1]}", number: f[2].to_i }
end
arr = ["John,Doe,11222019", "Mark,King,11232019", "Angle,Darma,11242019"]
arr.map do |item|
chunks = item.split(",")
{name: chunks[0...-1].join(" "), number: chunks[-1]}
end
Indexing by [0...-1] allows you to have variable number of items in the name part (middle name, or 2 piece last names) which is pretty common.

How to merge two arrays of hashes

I have two arrays of hashes:
a = [
{
key: 1,
value: "foo"
},
{
key: 2,
value: "baz"
}
]
b = [
{
key: 1,
value: "bar"
},
{
key: 1000,
value: "something"
}
]
I want to merge them into one array of hashes, so essentially a + b except I want any duplicated key in b to overwrite those in a. In this case, both a and b contain a key 1 and I want the final result to have b's key value pair.
Here's the expected result:
expected = [
{
key: 1,
value: "bar"
},
{
key: 2,
value: "baz"
},
{
key: 1000,
value: "something"
}
]
I got it to work but I was wondering if there's a less wordy way of doing this:
hash_result = {}
a.each do |item|
hash_result[item[:key]] = item[:value]
end
b.each do |item|
hash_result[item[:key]] = item[:value]
end
result = []
hash_result.each do |k,v|
result << {:key => k, :value => v}
end
puts result
puts expected == result # prints true
uniq would work if you concatenate the arrays in reverse order:
(b + a).uniq { |h| h[:key] }
#=> [
# {:key=>1, :value=>"bar"},
# {:key=>1000, :value=>"something"},
# {:key=>2, :value=>"baz"}
# ]
It doesn't however preserve the order.
[a, b].map { |arr| arr.group_by { |e| e[:key] } }
.reduce(&:merge)
.flat_map(&:last)
Here we use hash[:key] as a key to build the new hash, then we merge them overriding everything with the last value and return values.
I would rebuild your data a bit, since there are redundant keys in hashes:
thin_b = b.map { |h| [h[:key], h[:value]] }.to_h
#=> {1=>"bar", 1000=>"something"}
thin_a = b.map { |h| [h[:key], h[:value]] }.to_h
#=> {1=>"bar", 1000=>"something"}
Then you can use just Hash#merge:
thin_a.merge(thin_b)
#=> {1=>"bar", 2=>"baz", 1000=>"something"}
But, if you want, you can get exactly result as mentioned in question:
result.map { |k, v| { key: k, value: v } }
#=> [{:key=>1, :value=>"bar"},
# {:key=>2, :value=>"baz"},
# {:key=>1000, :value=>"something"}]
using Enumerable#group_by and Enumerable#map
(b+a).group_by { |e| e[:key] }.values.map {|arr| arr.first}
If you need to merge two arrays of hashes that should be merged also and there is more than two keys, then next snippet should help:
[a, b].flatten
.compact
.group_by { |v| v[:key] }
.values
.map { |e| e.reduce(&:merge) }

Ruby: which method to use for array of hashes to get a string with all hash values?

i have an array
[{name: "vivek"},{name:"ramesh"},{name: "suresh"},{name:"ganesh"}]
i want to create a method that return output as
"vivek,ramesh,suresh and ganesh".
if there are only two hashs like
[{name: "vivek"},{name: "ramesh"}]
output should be
"vivek and ramesh"
if names are more than 5 output should be "vivek, ramesh, suresh , nilash,ganesh etc .."
arr=[{name: 'vivek'},{name: 'Ganesh'},{name: 'Suresh'},{name: 'Ramesh'},{name: 'Bavesh'}]#,{name: 'Mukesh'}]
a = arr.size
case a
when 1
puts arr[0][:name]
when 2
puts arr[0][:name]+" and "+arr[1][:name]
when 3..5
ar = []
i=0
while i<arr.size-2
ar << arr[i]
i += 1
end
ar.each{|hash| printf "#{hash[:name]}"+","}
printf "#{arr[-2][:name]} and #{arr[-1][:name]}."
puts ""
when 6..100
ar = []
i=0
while i<4
ar << arr[i]
i += 1
end
ar.each{|hash| printf "#{hash[:name]}"+","}
printf "#{arr[4][:name]} etc..."
puts ""
else
puts "Error"
end
i did like this..but i know there should be a better way to do this with suitable methods can anybody help ..
I would do something like this:
def names_to_sentence(array)
names = array.map { |hash| hash[:name] }
case names.length
when (0..1)
names.first.to_s
when (2..5)
"#{names[0...-1].join(', ')} and #{names[-1]}"
else
"#{names[0..4].join(', ')} etc"
end
end
names_to_sentence([{name: "vivek"},{name:"ramesh"},{name: "suresh"},{name:"nilash"},{name:"ganesh"},{name: "foobar"}])
#=> "vivek, ramesh, suresh, nilash, ganesh etc"
names_to_sentence([{name: "vivek"},{name:"ramesh"},{name: "suresh"},{name:"ganesh"}])
#=> "vivek, ramesh, suresh and ganesh"
names_to_sentence([{name: "vivek"},{name: "ramesh"}])
#=> "vivek and ramesh"
names_to_sentence([{name: "vivek"}])
#=> "vivek"
names_to_sentence([])
#=> ""
def convert(arr)
case arr.size
when 0 then ""
when 1 then arr.first[:name]
else
*first, last = arr.map { |h| h[:name] }
first.join(', ') << (arr.size <= 5 ? " and #{last}" : ", #{last} ..etc")
end
end
arr = []
convert arr #=> ""
arr = [{name: "vivek"}]
convert arr #=> "vivek"
arr = [{name: "vivek"}, {name:"ramesh"}]
convert arr #=> "vivek and ramesh"
arr = [{name: "vivek"}, {name:"ramesh"}, {name: "suresh"}]
convert arr #=> "vivek, ramesh and suresh"
arr = [{name: "vivek"}, {name:"ramesh"}, {name: "suresh"}, {name:"ganesh"}]
convert arr #=> "vivek, ramesh, suresh and ganesh"
arr = [{name: "vivek"}, {name:"ramesh"}, {name: "suresh"}, {name:"ganesh"},
{name: "dog"}, {name: "cat"}]
convert arr #=> "vivek, ramesh, suresh, ganesh, cat, dog ..etc"
Working for 0, 1, 2 and 2+ names:
arr = [{name: "vivek"},{name:"ramesh"},{name: "suresh"},{name:"ganesh"}]
names = arr.map { |h| h[:name] }
str = [names.length>1 ? names[0..-2].join(", ") : nil,
names[-1]].compact.join(" and ")
Saw the "more than 5" case too late - left as an excercise to yourself. :)
λ = ->(input) do
output = input.flat_map(&:values)
if output.size > 5
output.take(5).join(', ') << ' etc.'
else
[output.pop, output.join(', ')].reverse.join(' and ')
end
end
λ.([{name: "vivek"},{name:"ramesh"},{name: "suresh"},{name:"ganesh"}])
#⇒ "vivek, ramesh, suresh and ganesh"
λ.([{name: "vivek"},{name:"ramesh"}])
#⇒ "vivek and ramesh"
input = [
{name: "vivek"},{name:"ramesh"},{name: "suresh"},{name:"ganesh"},
{name: "vivek"},{name:"ramesh"},{name: "suresh"},{name:"ganesh"}
]
λ.(input)
#⇒ "vivek, ramesh, suresh, ganesh, vivek etc."

Finding iterative intersection of array elements Perl

I want to find common elements in Arrays listed in a hash (Hash of arrays) in iterative manner and then divide each "intersection" by scalar value of first array. Iteration of intersection with other arrays for each array.
my #CS1= ("c1", "c2", "c3", "c4", "-c5");
my #CS2= ("c1", "c2", "c8", "c9");
my #CS3= ("c1", "c2", "c3");
my %CSHash= ( "set1" => [#CS1],
"set2"=> [#CS2],
"set3" => [#CS3],
);
My proposed Solution: But, it does not generate desired out put.
my %union=();
my %isect=();
my $cumLativeIsect=0;
foreach my $lst(keys %CSHash)
{
my $elCount=0;
foreach my $ele(#{$CSHash{$lst}})
{
$elCount++;
$union{$ele}++ && $isect{$ele}++;
}
my #intrsection= keys %isect;
if($elCount!=0 && scalar #intrsection!=0 )
{
$cumLativeIsect+= scalar #intrsection/$elCount;
}
}
Mathemetically, I am loking for following computation ( intr=intersection):
Intrsection=|{(cs1 intr cs1)/cs1+ (cs1 intr cs2)/cs1+ (cs1 intr cs3)/cs1}|+|{(cs2 intr cs2)/cs2+ (cs2 intr cs1)/cs2+ (cs2 intr cs3)/cs2}|+|{(cs3 intr cs1)/cs1+ (cs3 intr cs2)/cs1+ (cs3 intr cs3)/cs3}|
Here is a suggestion. I have renamed some of your variables, and used an array of array instead of a hash of arrays. After our discussion in the comments, I assumed you wanted to compute the following:
{|cs1 ∩ cs1|/|cs1| + |cs1 ∩ cs2|/|cs1| + |cs1 ∩ cs3|/|cs1| + ... }
+ {|cs2 ∩ cs1|/|cs2| + |cs2 ∩ cs2|/|cs2| + |cs2 ∩ cs3|/|cs2| + ... }
+ ...
Here is the code:
use strict;
use warnings;
use List::Util qw(any);
my #sets = (
[ "c1", "c2", "c3", "c4", "-c5"],
[ "c1", "c2", "c8", "c9"],
[ "c1", "c2", "c3"],
[ "c1", "c2", "c3"],
[ ],
[ ],
);
my $intr_sect = 0;
for my $set1 ( #sets ) {
my $N = scalar #$set1;
for my $set2 ( #sets ) {
my #intersect;
for my $item ( #$set2 ) {
if ( any { $_ eq $item } #$set1 ) {
push #intersect, $item;
}
}
$intr_sect += (scalar #intersect) / $N if $N;
}
}

Converting array of stringified key value pairs to hash in Ruby

I have some key-value pair strings in an array:
array = [ "Name = abc", "Id = 123", "Interest = Rock Climbing" ]
I need to convert it to a hash:
hash = { "Name" => "abc", "Id" => "123", "Interest" => "Rock Climbing" }
I must be doing something wrong because I'm getting weird mappings with my .shift.split resulting in {"Name=abc"=>"Id=123"}.
All you need to do is split each part of the array into a key and value (yielding an array of two-element arrays) and then pass the result to the handy Hash[] method:
arr = [ "Name = abc", "Id = 123", "Interest = Rock Climbing" ]
keys_values = arr.map {|item| item.split /\s*=\s*/ }
# => [ [ "Name", "abc" ],
# [ "Id", "123" ],
# [ "Interest", "Rock Climbing" ] ]
hsh = Hash[keys_values]
# => { "Name" => "abc",
# "Id" => "123",
# "Interest" => "Rock Climbing" }
You can do it this way (using Enumerable#each_with_object):
array.each_with_object({}) do |a, hash|
key,value = a.split(/\s*=\s*/) # splitting the array items into key and value
hash[key] = value # storing key => value pairs in the hash
end
# => {"Name"=>"abc", "Id"=>"123", "Interest"=>"Rock Climbing"}
If you find it little difficult to understand the each_with_object, you can do it in a naive way (Just accumulating the key and values in the result_hash):
result_hash = {}
array.each do |a|
key,value = a.split(/\s*=\s*/) # splitting the array items into key and value
result_hash[key] = value # storing key => value pairs in the result_hash
end
result_hash
# => {"Name"=>"abc", "Id"=>"123", "Interest"=>"Rock Climbing"}
Try this
array.map {|s| s.split('=')}.to_h
=> {"Name "=>" abc", "Id "=>" 123", "Interest "=>" Rock Climbing"}
array.each_with_object({}) { |s,h| h.update([s.split(/\s*=\s*/)].to_h) }
#=> {"Name"=>"abc", "Id"=>"123", "Interest"=>"Rock Climbing"}
For Ruby versions prior to 2.0 (when Array#to_h was introduced) replace [s.split(/\s*=\s*/)].h with Hash[[s.split(/\s*=\s*/)]].
The steps:
enum = array.each_with_object({})
#=> #<Enumerator: ["Name = abc", "Id = 123",
# "Interest = Rock Climbing"]:each_with_object({})>
We can see the elements of this enumerator by converting it to an array:
enum.to_a
#=> [["Name = abc", {}], ["Id = 123", {}], ["Interest = Rock Climbing", {}]]
The first element of enum is passed to the block, the block variables are assigned:
s,h = enum.next
#=> ["Name = abc", {}]
s #=> "Name = abc"
h #=> {}
and the block calculation is performed:
h.update([s.split(/\s*=\s*/)].to_h)
#=> h.update([["Name", "abc"]].to_h)
# {}.update({"Name"=>"abc"})
# {"Name"=>"abc"}
which is the updated value of h.
The next element of enum passed to the block is:
s,h = enum.next
#=> ["Id = 123", {"Name"=>"abc"}]
s #=> "Id = 123"
h #=> {"Name"=>"abc"}
h.update([s.split(/\s*=\s*/)].to_h)
#=> {"Name"=>"abc", "Id"=>"123"}
and so on.

Resources