I am using ActiveRecord. It has a handy method called group_by. When I use it with my activerecord objects, i get the below hash:
{["junior"]=>[#<Lead id: 1, created_at: "2015-02-13 02:34:39", updated_at: "2015-02-13 02:35:27", case_enabled: true>, #<Lead id: 2, created_at: "2015-02-13 20:48:19", updated_at: "2015-02-13 20:48:19", case_enabled: nil>, ["senior"]=>[#<Lead id: 3, created_at: "2015-02-13 20:48:19", updated_at: "2015-02-13 20:48:19", case_enabled: nil>, #<Lead id: 4, created_at: "2015-02-13 20:49:16", updated_at: "2015-02-13 20:49:16", case_enabled: nil>]}
However, I want a hash with subhashes that contain the collection as an ActiveRecord::Relation and column data. So this is what I come up with:
i = 0
r = group.reduce({}) do |acc, (k,v)|
h = {}
active_record_relation = where(id: v.map(&:id))
h["#{k.first}_collection"] = active_record_relation
h["#{k.first}_columns"] = Classification.where(code: k.first).first.default_fields
acc[i] = h
i += 1
acc
end
And it gives me the results I want:
{0=>{"junior_collection"=>#<ActiveRecord::Relation [# ... ]>, "junior_columns"=>[ ... ]}, 1=>{"senior_collection"=>#<ActiveRecord::Relation [# ... ]>, "senior_columns"=>[ ... ]}}
The fact that I had to add the i variable makes me feel like this is not the ruby way to do this. But I looked at the docs and I didn't find a way to add an index to reduce, since I am already passing a hash into reduce. Is there another way?
Your way is probably good enough but you can avoid separately tracking the index by doing .each.with_index.reduce(...) { |acc, ((k,v),i)| ... }, like so:
h = {'a' => 'b', 'c' => 'd', 'e' => 'f'}
h.each.with_index.reduce('OK') do |acc, ((k, v), i)|
puts "acc=#{acc}, k=#{k}, v=#{v}, i=#{i}"
acc
end
# acc=OK, k=a, v=b, i=0
# acc=OK, k=c, v=d, i=1
# acc=OK, k=e, v=f, i=2
# => "OK"
Not sure if it's more Rubyish than your way =\
Related
I have an array like this:
arr = [{id: 1, name: 'John' }, {id: 2, name: 'Sam' }, {id: 3, name: 'Bob' }]
I need to check if any of arr objects have name Sam. What is the most elegant way? I can only think of cycling with each.
I need to check if any of arr objects have name Sam
Enumerable#any? is a good way to go.
arr = [ {id: 1, name: 'John' }, {id: 2, name: 'Sam' }, {id: 3, name: 'Bob' }]
arr.any? {|h| h[:name] == "Sam"}
# => true
Now if you also want to see which Array object has the value Sam in it,you can use Enumerable#find for the same:
arr.find {|h| h[:name] == "Sam"}
# => {:id=>2, :name=>"Sam"}
You can also choose select or count methods
Enumberable#select
> arr = [{id: 1, name: 'John' }, {id: 2, name: 'Sam' }, {id: 3, name: 'Bob' }]
> arr.select { | h | h[:name] == 'Sam' }
# => [{:id=>2, :name=>"Sam"}]
Enumberable#count
> arr.count { | h | h[:name] == 'Sam' }
# => 1
You can use Enumberable#find_all to return all object that match the constrain
arr = [{:id=>1,:first_name=>'sam'},{:id=>2,:first_name=>'sam'},{:id=>3,:first_name=>'samanderson'},{:id=>4,:first_name=>'samuel'}]
arr.find_all{|obj| obj.first_name == 'sam'}
# => [{:id=>1,:first_name=>'sam'},{:id=>2,:first_name=>'sam'}]
I have an Array of Hashes with the same keys, storing people's data.
I want to remove the hashes that have the same values for the keys :name and :surname. The rest of the values can differ, so calling uniq! on array won't work.
Is there a simple solution for this?
You can pass a block to uniq or uniq!, the value returned by the block is used to compare two entries for equality:
irb> people = [{name: 'foo', surname: 'bar', age: 10},
{name: 'foo', surname: 'bar' age: 11}]
irb> people.uniq { |p| [p[:name], p[:surname]] }
=> [{:name=>"foo", :surname=>"bar", :age=>10}]
arr=[{name: 'john', surname: 'smith', phone:123456789},
{name: 'thomas', surname: 'hardy', phone: 671234992},
{name: 'john', surname: 'smith', phone: 666777888}]
# [{:name=>"john", :surname=>"smith", :phone=>123456789},
# {:name=>"thomas", :surname=>"hardy", :phone=>671234992},
# {:name=>"john", :surname=>"smith", :phone=>666777888}]
arr.uniq {|h| [h[:name], h[:surname]]}
# [{:name=>"john", :surname=>"smith", :phone=>123456789},
# {:name=>"thomas", :surname=>"hardy", :phone=>671234992}]
unique_people = {}
person_array.each do |person|
unique_people["#{person[:name]} #{person[:surname]}"] = person
end
array_of_unique_people = unique_people.values
This should do the trick.
a.delete_if do |h|
a.select{|i| i[:name] == h[:name] and i[:surname] == h[:surname] }.count > 1
end
I have an Array of objects:
[
#<User id: 1, name: "Kostas">,
#<User id: 2, name: "Moufa">,
...
]
And I want to convert this into an Hash with the id as the keys and the objects as the values. Right now I do it like so but I know there is a better way:
users = User.all.reduce({}) do |hash, user|
hash[user.id] = user
hash
end
The expected output:
{
1 => #<User id: 1, name: "Kostas">,
2 => #<User id: 2, name: "Moufa">,
...
}
users_by_id = User.all.map { |user| [user.id, user] }.to_h
If you are using Rails, ActiveSupport provides Enumerable#index_by:
users_by_id = User.all.index_by(&:id)
You'll get a slightly better code by using each_with_object instead of reduce.
users = User.all.each_with_object({}) do |user, hash|
hash[user.id] = user
end
You can simply do (using the Ruby 3 syntax of _1)
users_by_id = User.all.to_h { [_1.id, _1] }
I have an array in my Rails 3.1 apps that has made by several objects:
[#<Hardware id: 10, brand_id: 5, model: "B4200", description: "Stampante OKI B4200", typology_id: 3, sub_typology_id: 10, created_at: nil, updated_at: nil>, #<Hardware id: 19, brand_id: 9, model: "JetLab", description: "JetLab - 600 ", typology_id: 5, sub_typology_id: nil, created_at: nil, updated_at: nil>]
and I want remove one object from this array. Using Rails console, I've tried to do something such as (try to remove first object):
array.pop=#<Hardware id: 10, brand_id: 5, model: "B4200", description: "Stampante OKI B4200", typology_id: 3, sub_typology_id: 10, created_at: nil, updated_at: nil>
but it doesn't work. How can I do this?
UPDATED: My goal isn't to pop last element on array, but a generic object (everywhere inside array) that I should find using mysql search query.
my_array = [ 1, 2, 3 ]
item = my_array.pop
puts item
# => 3
puts my_array
# => [ 1, 2 ]
You probably want to use the Array#delete function
an_array = [1,3,4]
an_array.delete(3)
# => 3
puts an_array
# => [1,4]
Check it out in the Ruby documentation:
http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-delete
This question already has answers here:
Value inside array having same object id [duplicate]
(2 answers)
Closed 11 months ago.
ruby 1.8.7 (2008-08-11 patchlevel 72) [i586-linux]
From script/console:
>> pairs = Array.new(2).map!{Array.new(2).map!{Array.new(2, Array.new)}}
=> [[[[], []], [[], []]], [[[], []], [[], []]]]
>> pair = Pair.first
=> #<Pair id: 39, charge_card_id: 1, classroom_id: 1, timeslot_id: 1, created_at: "2010-04-01 00:45:37", updated_at: "2010-04-01 00:45:47">
>> pairs[0][0][0] << pair
=> [#<Pair id: 39, charge_card_id: 1, classroom_id: 1, timeslot_id: 1, created_at: "2010-04-01 00:45:37", updated_at: "2010-04-01 00:45:47">]
>> pairs[0][0]
=> [[#<Pair id: 39, charge_card_id: 1, classroom_id: 1, timeslot_id: 1, created_at: "2010-04-01 00:45:37", updated_at: "2010-04-01 00:45:47">], [#<Pair id: 39, charge_card_id: 1, classroom_id: 1, timeslot_id: 1, created_at: "2010-04-01 00:45:37", updated_at: "2010-04-01 00:45:47">]]
>>
So the question is why the pair object appears in pairs[0][0][0] AND in pairs[0][0][1] inspite of I did'n ask it to appear there. Notice I don't nedd to pairs[0][0][0] = pair - I want it as a first array member, so I need to use << or .push.
First of all, you want
pairs = Array.new(2) { Array.new(2) { Array.new(2) { [] }}}
instead of what you got. Two major differences:
you save yourself the #map! calls
in your example, "Array.new(2, Array.new)" is creating one Array which is used for both indices, so you are refering to the same array twice. By using the block syntax, you are ensuring that for every index you are having one separate instance of Array
Now works with:
pairs = Array.new(2).map!{Array.new(2).map!{Array.new(2).map!{Array.new}}}
I think it's because of the deepest arrays was just links to memory pointer.
Array.new(2, Array.new) gives you an array of size 2, with a copy of the same empty array object at both index 0 and index 1. If you want them to be different objects, use map! like you've done at the higher levels.