most efficient ruby way to transform this array? - ruby-on-rails

I have an array in the following form:
[\"id\", 545, \"program_name\", \"VILLIANS MARATHON\", \"episode_name\", \"1-Season1:Ep.11\"]
I need to transform it to the form below:
[545, \"VILLIANS MARATHON\", \"1-Season1:Ep.11\"]
The way Im doing this is as follows:
#Convert a Active record hash to a 2D array
def activerecord_hash_to_datatable_array(activerecord_resultset)
array_of_arrays = Array.new()
array_of_rs_hashes = activerecord_resultset.to_a.map(&:serializable_hash)
array_of_rs_hashes.each do |rs|
# {"id"=>1594, "program_name"=>nil, "episode_name"=>nil}
rs = rs.flatten
#[\"id\", 545, \"program_name\", \"MARATHON\", \"episode_name\", \"1-Season1:Ep.11\"]"
rs_array = Array.new()
index = 1
while index < rs.length
puts "index = #{index}"
puts "\033[0;33m"+"#{rs[index]}"+"\033[0;37m"
log_with_yellow("index[#{index}] " + "#{rs[index]}")
rs_array << rs[index]
index += 2
end
array_of_arrays << rs_array
end
array_of_arrays
end
I was wondering what the most efficient way to accomplish this is.
Clearly I need to retain only odd elements. But Id like to avoid iterating over all elements and comparing each elements index.
Is there a way to do this by skipping all the even elements ?
Thanks

You can do the following:
your_array.values_at(*your_array.each_index.select(&:odd?))
=> [545, "VILLIANS MARATHON", "1-Season1:Ep.11"]

require 'json'
arr = JSON.parse("[\"id\", 545, \"program_name\", \"VILLIANS MARATHON\", \"episode_name\", \"1-Season1:Ep.11\"]")
new_arr = arr.select.with_index { |x,i| i.odd? }
p new_arr
# >> [545, "VILLIANS MARATHON", "1-Season1:Ep.11"]

If array_of_rs_hashes is indeed an array of hashes, can't you just do:
res = array_of_rs_hashes.map(&:values)

Yep there is :
require 'json'
Hash[*JSON.parse(s)].values #=> [545, "VILLIANS MARATHON", "1-Season1:Ep.11"]
where s = "[\"id\", 545, \"program_name\", \"VILLIANS MARATHON\", \"episode_name\", \"1-Season1:Ep.11\"]"

Try:
your_2d_array.map {|a| a.each_slice(2).map {|key, value| value}}
If you ahve active support, you can write it slightly more readible:
your_2d_array.map {|a| a.each_slice(2).map(&:second)}

Related

creating an array using find method in rails

input = {"color"=>["red"],"size"=>["s","l"]}
json_obj = [{"color":"red","id":"123","size":"s","name":"test"},
{"color":"yellow","id":"124","size":"s","name":"test"},
{"color":"red","id":"125","size":"l","name":"test"}]
Output should be
output["red_s"] = {"color":"red","id":"123","size":"s","name":"test"}
output["red_l"] = {"color":"red","id":"125","size":"l","name":"test"}
output is the combinations of the input and a find on the json_obj.
How to get the output in rails?
I have the below script to get the combinations ie.red_s and red_l,
ary = input.map {|k,v| [k].product v}
output = ary.shift.product(*ary).map {|a| Hash[a]}
And
output[red_s]=json_obj.find{|h| h["color"] == "red" and h["size"] == "S"}
I don't want to have any hardcodings in code like color and size as above.
I think this should get you close to what you want.
Note the "ticks" around your json array object (what you had is not valid ruby)
The other issue is you would have to figure a better way to create the output hash key.
require 'json'
input = {"color"=>["red"],"size"=>["s","l"]}
output = {}
json_obj = '[{"color":"red","id":"123","size":"s","name":"test"},
{"color":"yellow","id":"124","size":"s","name":"test"},
{"color":"red","id":"125","size":"l","name":"test"}]'
found = JSON.parse json_obj
input.each_key do |key|
found = found.select { |item| input[key].include?(item[key]) }
end
puts found
found.each do |item|
output_key = ""
input.each_key do |key|
output_key = "#{item[key]}_" + output_key
end
output["#{output_key}"] = item.to_json
end
puts output

Counting several elements inside an array

I just wrote a method that I'm pretty sure is terribly written. I can't figure out if there is a better way to write this in ruby. It's just a simple loop that is counting stuff.
Of course, I could use a select or something like that, but that would require looping twice on my array. Is there a way to increment several variables by looping without declaring the field before the loop? Something like a multiple select, I don't know. It's even worst when I have more counters.
Thank you!
failed_tests = 0
passed_tests = 0
tests.each do |test|
case test.status
when :failed
failed_tests += 1
when :passed
passed_tests +=1
end
end
You could do something clever like this:
tests.each_with_object(failed: 0, passed: 0) do |test, memo|
memo[test.status] += 1
end
# => { failed: 1, passed: 10 }
You can use the #reduce method:
failed, passed = tests.reduce([0, 0]) do |(failed, passed), test|
case test.status
when :failed
[failed + 1, passed]
when :passed
[failed, passed + 1]
else
[failed, passed]
end
end
Or with a Hash with default value, this will work with any statuses:
tests.reduce(Hash.new(0)) do |counter, test|
counter[test.status] += 1
counter
end
Or even enhancing this with #fivedigit's idea:
tests.each_with_object(Hash.new(0)) do |test, counter|
counter[test.status] += 1
end
Assuming Rails 4 ( using 4.0.x here). I would suggest:
tests.group(:status).count
# -> {'passed' => 2, 'failed' => 34, 'anyotherstatus' => 12}
This will group all records by any possible :status value, and count each individual ocurrence.
Edit: adding a Rails-free approach
Hash[tests.group_by(&:status).map{|k,v| [k,v.size]}]
Group by each element's value.
Map the grouping to an array of [value, counter] pairs.
Turn the array of paris into key-values within a Hash, i.e. accessible via result[1]=2 ....
hash = test.reduce(Hash.new(0)) { |hash,element| hash[element.status] += 1; hash }
this will return a hash with the count of the elements.
ex:
class Test
attr_reader :status
def initialize
#status = ['pass', 'failed'].sample
end
end
array = []
5.times { array.push Test.new }
hash = array.reduce(Hash.new(0)) { |hash,element| hash[element.status] += 1; hash }
=> {"failed"=>3, "pass"=>2}
res_array = tests.map{|test| test.status}
failed_tests = res_array.count :failed
passed_tests = res_array.count :passed

Ruby how to get partial hash

If I have a hash in ruby like this,
first = {:a=>1,:b=>2,:c=>3,:d=>4,:e=>5}
How can I achieve this by single line script
second = {:a=>1,:c=>3,:e=>5}
Thank you very much.
Exactly what you want in a single pretty line of code
second = first.slice(:a, :c, :e) # => {:a=>1, :c=>3, :e=>5}
EDIT: previous answer was using Rails. This is a solution just using Ruby
second = first.delete_if {|k,v| ![:a, :c, :e].include?(k) } # => {:a=>1, :c=>3, :e=>5}
Try delete_if or keep_if, both of them are part of core Ruby. Both of them operate on the current hash. slice is also part of core Ruby already.
first = {:a=>1,:b=>2,:c=>3,:d=>4,:e=>5}
first_clone = first.clone
p first.keep_if { |key| [:a, :c, :e].include?(key) } # => {:a=>1,:c=>3,:e=>5}
p first_clone.delete_if { |key, value| [:b, :d, :f].include?(key) } # => {:a=>1,:c=>3,:e=>5}
Documentation:
delete_if
keep_if
slice
first.keep_if{|key| [:a,:c,:d].include?(key)}
Method 1
first = {:a=>1,:b=>2,:c=>3,:d=>4,:e=>5}
first.delete(:b)
first.delete(:d)
second = first
Method 2
first = {:a=>1,:b=>2,:c=>3,:d=>4,:e=>5}
second = first.delete_if {|key, value| key == :b || key == :d }
Assuming you don't know the keys you want to keep / remove ... you could do it like this:
first = {:a=>1,:b=>2,:c=>3,:d=>4,:e=>5}
iterator = 0
second = {}
first.each_pair do |key, value|
second[key] = value if iterator % 2 == 0
iterator += 1
end
second # is now {:a=>1,:c=>3,:e=>5}

How do you call an index in .each_with_index

Not sure this isn't working.
>> params[:payments]
{"0"=>{":amount_paid"=>"80.00", ":date_paid"=>"2/27/2008"}, "1"=>{":amount_paid"=>"100.00", ":date_paid"=>"5/8/2008"}}
So I can call a specific object with this :
>> params[:payments][:"1"]
{":amount_paid"=>"100.00", ":date_paid"=>"5/8/2008"}
But if I write this..
>> params[:payments].each_with_index{|item, idx| item[:"#{idx}"]}
TypeError Exception: Symbol as array index
Idealistically, I want to accomplish this :
params[:payments].each_with_index do |item, idx|
#calc.payments[idx][:date_paid] = item[:"#{idx}"][":amount_paid"]
#calc.payments[idx][:amount_paid] = (item[:"#{idx}"][":amount_paid"]).to_f
end
Update:
Based on some answers, I'ved tried this :
params[:payments].each{|k,v| #calc.payments[k.to_i] = v[":amounts_paid"]}
This turns #calc.payments into :
nil
nil
Backing up though, the others seem to work..
>> params[:payments].each{|k,v| p v[":amount_paid"]}
"80.00"
"100.00"
And this one..
>> params[:payments].each{|k,v| p #calc.payments[k.to_i]}
{:date_awarded=>"1/2/2008", :judgement_balance=>1955.96}
nil
How can I access item[idx] in a loop?
params[:payments].each do |k,v|
puts "Item %d amount=%s date=%s\n" % [k, v[":amount_paid"], v[":date_paid"]]
end
Item 0 amount=80.00 date=2/27/2008
Item 1 amount=100.00 date=5/8/2008
Update:
Ok, ok, here is a complete program .. script .. that you can actually run. Since you are trying to make sense of Ruby I think you should work with it outside of Rails for a few minutes. I mocked up #calc.payments, whatever that is. This code will run and apparently do what you want.
require 'pp'
(params = {})[:payments] = {"0"=>{":amount_paid"=>"80.00", ":date_paid"=>"2/27/2008"}, "1"=>{":amount_paid"=>"100.00", ":date_paid"=>"5/8/2008"}}
pp params
class T; attr_accessor :payments; end
(#calc = T.new).payments = []
params[:payments].each do |k,v|
i = k.to_i
#calc.payments[i] ||= {}
#calc.payments[i][:date_paid] = v[":date_paid"]
#calc.payments[i][:amount_paid] = v[":date_paid"].to_f
end
pp #calc.payments
If you run it you should see:
{:payments=>
{"0"=>{":amount_paid"=>"80.00", ":date_paid"=>"2/27/2008"},
"1"=>{":amount_paid"=>"100.00", ":date_paid"=>"5/8/2008"}}}
[{:date_paid=>"2/27/2008", :amount_paid=>2.0},
{:date_paid=>"5/8/2008", :amount_paid=>5.0}]
You could just do a this to access the values. Since params[:payments] contains a hash, then for each pass through, key, will be assigned the "0", "1", etc., and value will be assigned the hash with amount_paid and date_paid.
params[:payments].each do |key, value|
amount_paid = value[":amount_paid"]
...
end

Ruby on Rails: Array to Hash with (key, array of values)

Lets say I have an Array of content_categories (content_categories = user.content_categories)
I now want to add every element belonging to a certain categorie to content_categories with the category as a key and the the content-item IDs as elements of a set
In PHP something like this is possible:
foreach ($content_categories as $key => $category) {
$contentsByCategoryIDArray = Category.getContents($category[id])
$content_categories[$key][$contentsByCategoryIDArray]
}
Is there an easy way in rails to do this?
Greets,
Nico
Your question isn't really a Rails question, it's a general Ruby programming question.
Your description isn't very clear, but from what I understand, you want to group IDs for common categories using a Hash. There are various other ways of doing this, but this is easy to understand::
ary = [
'cat1', {:id => 1},
'cat2', {:id => 2},
'cat1', {:id => 3}
]
hsh = {}
ary.each_slice(2) { |a|
key,category = a
hsh[key] ? hsh[key] << category[:id] : hsh[key] = [category[:id]]
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
I'm using a simple Array with a category, followed by a simple hash representing some object instance, because it makes it easy to visualize. If you have a more complex object, replace the hash entries with those objects, and tweak how you access the ID in the ternary (?:) line.
Using Enumerable.inject():
hsh = ary.each_slice(2).inject({}) { |h,a|
key,category = a
h[key] ? h[key] << category[:id] : h[key] = [category[:id]]
h
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
Enumerable.group_by() could probably shrink it even more, but my brain is fading.
I'd use Enumerable#inject
content_categories = content_categories_array.inject({}){ |memo, category| memo[category] = Category.get_contents(category); memo }
Hash[content_categories.map{|cat|
[cat, Category.get_contents(cat)]
}]
Not really the right answer, because you want IDs in your array, but I post it anyway, because it's nice and short, and you might actually get away with it:
content_categories.group_by(&:category)
content_categories.each do |k,v|
content_categories[k] = Category.getContents(v)
end
I suppose it's works
If i understand correctly, content_categories is an array of categories, which needs to be turned into a hash of categories, and their elements.
content_categories_array = content_categories
content_categories_hash = {}
content_categories_array.each do |category|
content_categories_hash[category] = Category.get_contents(category)
end
content_categories = content_categories_hash
That is the long version, which you can also write like
content_categories = {}.tap do |hash|
content_categories.each { |category| hash[category] = Category.get_contents(category) }
end
For this solution, content_categories must be a hash, not an array as you describe. Otherwise not sure where you're getting the key.
contents_by_categories = Hash[*content_categories.map{|k, v| [k, Category.getContents(v.id)]}]

Resources