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
Approach for Installing System Service Implemented as Ruby Gem
Determining Whether One Array Contains the Contents of Another Array in Ruby
Zip Up All Paperclip Attachments Stored on S3
What's the Best Way to Start a Background Process, That Can Get Accessed Later On
Made a Mistake Installing Rvm with Sudo. How to Reverse
Why Is a String Key for a Hash Frozen
Disable Devise's :Confirmable On-The-Fly to Batch-Generate Users
What's Gem Can Operate Excel on Linux
Change The Ruby Process Name in Top
Get Time from Datetime Variable in Ruby
How to Create a Rails 3 Route That Will Match All Requests and Direct to One Resource/Page
Error While Installing Ruby 1.9.3
Rails Server Does Not Start -> Could Not Find a JavaScript Runtime
Ruby on Rails - Rack-Cors Multiple Origins with Different Resources
Vim Syntax Highlighting for Ruby 1.9