Ruby: How to Join Elements of an Array Together with a Prefix

Ruby: How do I join elements of an array together with a prefix?

If your array is in a then this one-liner will do it

a.map { |k| "%#{k}" }.join("_")

You could easily put this in a function of your own - even add it to the Array class so that you can call it on an array, like in your example.

Note that the '!' version of map (map!) will modify the array in place - perhaps not your intent.

Dynamic string generation with depending on size of array

def normalized_manager_ids
if manager_id_list.count <= 1
"ManagerID='#{manager_id_list.first}'"
else
manager_id_list.map{ |id| "ManagerID='#{id}'" }.join(" OR ")
end
end

or even shorter:

def normalized_manager_ids
manager_id_list.map{ |id| "ManagerID='#{id}'" }.join(" OR ")
end

However, if you wan't to use this in SQL query, I would rather recommend using SQL IN operator instead - for example:

.where("ManagerID IN (?)", manager_id_list)

Determining if a prefix exists in a set

There's a little known magical module in Ruby called Abbrev.

require 'abbrev'

abbreviations = Abbrev::abbrev([
"Alice",
"Bob",
"C",
"Ca",
"Car",
"Carol",
"Caroling",
"Carousel"
])
carolers = Abbrev::abbrev(%w[Carolers])
(carolers.keys - abbreviations.keys).sort.first # => "Caro"

Above I took the first element but this shows what else would be available.

pp (carolers.keys - abbreviations.keys).sort 
# >> ["Caro", "Carole", "Caroler", "Carolers"]

Wrap all the above in a function, compute the resulting missing elements, and then iterate over them yielding them to a block, or use an enumerator to return them one-by-one.

This is what is generated for a single word. For an array it is more complex.

require 'pp'
pp Abbrev::abbrev(['cat'])
# >> {"ca"=>"cat", "c"=>"cat", "cat"=>"cat"}

pp Abbrev::abbrev(['cat', 'car', 'cattle', 'carrier'])
# >> {"cattl"=>"cattle",
# >> "catt"=>"cattle",
# >> "cat"=>"cat",
# >> "carrie"=>"carrier",
# >> "carri"=>"carrier",
# >> "carr"=>"carrier",
# >> "car"=>"car",
# >> "cattle"=>"cattle",
# >> "carrier"=>"carrier"}

How to insert prefix to all array elements but first and last?

You could do it like this:

result[1 ... -1].each { |s| s[0, 0] = ' - ' }

Note that there are three dots, not two. This works because s[0,0] modifies s in-place. If you're using 1.9.3+, you could also (and probably should) use prepend:

result[1 ... -1].each { |s| s.prepend(' - ') }

And, as the Tin Man notes in the comments, you could also use the double-dot range operator which is more common:

result[1 .. -2].each { |s| s[0, 0] = ' - ' }
result[1 .. -2].each { |s| s.prepend(' - ') }

Which range operator you use is a matter of preference.

How can I merge duplicate elements of array while keeping the values combined?

This is one way among many that you could do that.

Code

def combine(prods)
prods.map(&:flatten)
.each_with_object(Hash.new {|h,k| h[k]=[]}) { |(k,v),h| h[k] << v }
.map { |k,v| { k=>v } }
end

Examples

For your value of prods:

combine(prods)   
#=> [{"1050"=>[{"key"=>"value", "key2"=>"value2"},
# {"key"=>"value", "key2"=>"value2"}]},
# {"6650"=>[{"key"=>"value", "key2"=>"value2"},
# {"key"=>"value", "key2"=>"value2"}]}]

Now let's redefine prods:

prods = [{"1050" => {"keya" => "value1", "keyb" => "value1"}},
{"1050" => {"keya" => "value2", "keyb" => "value2"}},
{"6650" => {"keya" => "value3", "keyb" => "value3"}},
{"6650" => {"keya" => "value4", "keyb" => "value4"}}]
combine(prods)
#=> [{"1050"=>[{"keya"=>"value1", "keyb"=>"value1"},
# {"keya"=>"value2", "keyb"=>"value2"}]},
# {"6650"=>[{"keya"=>"value3", "keyb"=>"value3"},
# {"keya"=>"value4", "keyb"=>"value4"}]}]

Explanation

These are the steps:

a = prods.map(&:flatten)
#=> [["1050", {"key"=>"value", "key2"=>"value2"}],
# ["1050", {"key"=>"value", "key2"=>"value2"}],
# ["6650", {"key"=>"value", "key2"=>"value2"}],
# ["6650", {"key"=>"value", "key2"=>"value2"}]]

h = a.each_with_object(Hash.new {|h,k| h[k]=[]}) { |(k,v),h| h[k] << v }
#=> {"1050"=>[{"key"=>"value", "key2"=>"value2"},
# {"key"=>"value", "key2"=>"value2"}],
# "6650"=>[{"key"=>"value", "key2"=>"value2"},
# {"key"=>"value", "key2"=>"value2"}]}

Lastly,

h.map { |k,v| { k=>v } }

produces the result shown above.

In computing h Enumerable#each_with_object's object is the value of the block variable h. Initially, h is an empty hash defined as follows:

Hash.new {|h,k| h[k]=[]}

The block gives the hash's default value. This says that if h is the hash and k is a key to be added to the hash, it's default value is an empty array. The first value of a passed to each_with_object's block is:

["1050", {"key"=>"value", "key2"=>"value2"}]

The block variables are therefore assigned as follows:

(k,v),h = [["1050", {"key"=>"value", "key2"=>"value2"}], {}]
#=> [["1050", {"key"=>"value", "key2"=>"value2"}], {}]
k #=> "1050"
v #=> {"key"=>"value", "key2"=>"value2"}
h #=> {}

and the block calculation is:

h[k] << v

which is:

h["1050"] << {"key"=>"value", "key2"=>"value2"}

Since h does not have a key "1050", h["1050"] is first assigned its default value, an empty hash, so we have:

(h["1050"] = []) << {"key"=>"value", "key2"=>"value2"}

The hash h is now:

h #=> { "1050"=>[{"key"=>"value", "key2"=>"value2"}] }

The next value of a is passed to the block, causing the block variables being to be updated as follows:

(k,v),h = [["1050", {"key"=>"value", "key2"=>"value2"}],
{ "1050"=>[{"key"=>"value", "key2"=>"value2"}] }]
k #=> "1050"
v #=> {"key"=>"value", "key2"=>"value2"}
h #=> {"1050"=>[{"key"=>"value", "key2"=>"value2"}]}

The block calculation is therefore:

h[k] << v
# h["1050"] << {"key"=>"value", "key2"=>"value2"}

As h now has the key "1050" (whose value is an array), the default value is not used and the hash h becomes

h #=> {"1050"=>[{"key"=>"value", "key2"=>"value2"},
# {"key"=>"value", "key2"=>"value2"}]}

The remaining calculations are performed similarly.

Group strings with similar pattern in Ruby

arr.group_by { |x| x[/[a-zA-Z]+/] }.values

concatenate with splat ruby

When accepting arguments via splat, they will always be an array. So you can simply add the two arrays together.

def add(*numbers)
arr + numbers
end

Get a list of all the prefixes of a string

A quick benchmark:

require 'fruity'

string = 'ruby'

compare do

toro2k do
string.size.times.collect { |i| string[0..i] }
end

marek_lipka do
(0...(string.length)).map{ |i| string[0..i] }
end

jorg_w_mittag do
string.chars.inject([[], '']) { |(res, memo), c|
[res << memo += c, memo]
}.first
end

jorg_w_mittag_2 do
acc = ''
string.chars.map {|c| acc += c }
end

stefan do
Array.new(string.size) { |i| string[0..i] }
end

end

And the winner is:

Running each test 512 times. Test will take about 1 second.
jorg_w_mittag_2 is faster than stefan by 19.999999999999996% ± 10.0%
stefan is faster than marek_lipka by 10.000000000000009% ± 10.0%
marek_lipka is faster than jorg_w_mittag by 10.000000000000009% ± 1.0%
jorg_w_mittag is similar to toro2k

Ruby: How to prefix Namespaces in hash

Here's an approach where you pass in a list of identifiers associated with each level of nesting:

def classify(o, with)
case o
when Hash
h = {}
o.each {|k,v| h[:"#{with[0]}:#{k}"] = classify(v, with[1, with.length])}
h
else
o.class
end
end

hash = {:UserTicket=>'123',:ImpersonationUsername=>'dave',:TicketSettings=>{:ResourceId=>'abcd',:ClientIp=>'0',:Username=>'bobby'}}

classify(hash, [ :mes, :data ])
# => {"mes:UserTicket"=>String, "mes:ImpersonationUsername"=>String, "mes:TicketSettings"=>{"data:ResourceId"=>String, "data:ClientIp"=>String, "data:Username"=>String}}

If you're using a recursive algorithm you have the opportunity to modify the scope of what's being applied with each level you dig down.

match table row id's with a common prefix

To get the tr elements that have an id starting with "rowId_":

pageC1.search('//tr[starts-with(@id, "rowId_")]')


Related Topics



Leave a reply



Submit