Remove element from array - ruby

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

Related

How to assign multiple attributes to ActiveRecord::Relation array in single step?

There is a function is rails to assign attributes to a Model ActiveRecord instance(single record) without saving(or updating).
For example:
$> post = Post.last #=> #<Post id: 53, title: 'Alpha', user_id: 39, state: 'published'>
$> post.assign_attributes(state: 'pending') #=> nil
$> post #=> #<Post id: 53, title: 'Alpha', user_id: 39, state: 'pending'>
but post is not saved in db.
Same I want to do with multiple records of ActiveRecord::Relation.
For example:
$> posts = Post.where(state: 'published')
#=> #<ActiveRecord::Relation [#<Post id: 50, title: 'Wonderful', user_id: 39, state: 'published'>, #<Post id: 53, title: 'Alpha', user_id: 39, state: 'published'>]>
$> posts.assign_attributes(state: 'pending') #=> nil
$> posts
#=> #<ActiveRecord::Relation [#<Post id: 50, title: 'Wonderful', user_id: 39, state: 'pending'>, #<Post id: 53, title: 'Alpha', user_id: 39, state: 'pending'>]>
You cannot do mass assignment in rails like this
posts.each {|post| post.assign_attributes(state: 'pending')}
each perform operation and do not change nature for posts. So posts will be ActiveRecord::Relation object.
When you use Post.update_all this is special, because it runs an UPDATE ALL SQL statement in the database. Therefore there's no need to iterate over the collection in memory.
Updating all records in ruby memory, on the other hand, cannot do this; you do need to iterate over them:
posts.each { |post| post.assign_attributes(state: 'pending') }
However, this shouldn't be an issue. I can't think of any scenario where you'd want to update a collection of objects in memory, without also wanting to iterate over them at some point.

adding a reduce_with_index iterator to ruby

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 =\

Sort an array of objects in ruby

First of all full disclosure. I attempted to solve this issue at this thread but I wanted to repost in hopes of getting some help in Ruby:
best way to sort an array of objects by category and infinite subcategory
I have made a feeble attempt a that solution but can't even get close and it's not even worth showing. Below is my attempt. It would mean the world to me if someone could give some good direction on this.
data = [{id: 1, name: "parent test 1", parent_id: nil, top_level_category_id: nil},
{id: 2, name: "test 2", parent_id: 1, top_level_category_id: 1},
{id: 3, name: "test 3", parent_id: 1, top_level_category_id: 1},
{id: 4, name: "parent test 4", parent_id: nil, top_level_category_id: nil},
{id: 5, name: "test 5", parent_id: 3, top_level_category_id: 4},
{id: 6, name: "test 6", parent_id: 4, top_level_category_id: 4},
{id: 7, name: "test 7", parent_id: 4, top_level_category_id: 4}]
This is what I am hoping to accomplish
parent test 1
test 2
test 3
test 5
parent test 2
test 6
test 7
-
ord_cat = {}
for item in data
if item[:parent_id] == nil
ord_cat[item[:id]] = {:name => item[:name], :children => {}}
end
end
# fill child directories
for item in data
if item[:parent_id] != nil
ord_cat[item[:top_level_category_id]][:children].merge!({item[:id] => item[:name]})
end
end
puts ord_cat
This is the output
{1=>{:name=>"parent test 1", :children=>{2=>"test 2", 3=>"test 3", 5=>"test 5"}}, 4=>{:name=>"parent test 4", :children=>{6=>"test 6", 7=>"test 7"}}}
This is clearly not nesting "test 5" properly. I'm not thrilled with the structure of the object.
We can construct the ancestor list for each node and sorting based on that list, in effect giving a depth-first traversal of the tree.
A bit lengthy solution, but would solve the problem I believe. Have used hash more to avoid scanning the array multiple times.
data = [{id: 1, name: "parent test 1", parent_id: nil, top_level_category_id: nil},
{id: 2, name: "test 2", parent_id: 1, top_level_category_id: 1},
{id: 3, name: "test 3", parent_id: 1, top_level_category_id: 1},
{id: 4, name: "parent test 4", parent_id: nil, top_level_category_id: nil},
{id: 5, name: "test 5", parent_id: 3, top_level_category_id: 4},
{id: 6, name: "test 6", parent_id: 4, top_level_category_id: 4},
{id: 7, name: "test 7", parent_id: 4, top_level_category_id: 4}]
# Amount of indentation for printing each node's nesting
INDENTATION_PER_LEVEL = 1
id_to_data_map = {}
id_to_parent_id_map = {}
data.each { |d|
id_to_data_map[d[:id]] = d
id_to_parent_id_map[d[:id]] = d[:parent_id]
}
data_with_ancestors = {}
data.each do |record|
ancestors = [record[:name]]
# Temporary parent
parent = record
while true do
parent_id = id_to_parent_id_map[parent[:id]]
break if parent_id.nil? # Hit the root - get out.
parent = id_to_data_map[parent_id]
ancestors << parent[:name]
end
# Construct a list of ancestors for the node, with the oldest ancestor first.
data_with_ancestors[record[:name]] = ancestors.reverse
end
# Sort the flattened list based on the ancestor string constructed by joining all the parent names.
sorted_list = data_with_ancestors.sort_by {|name, ancestors| ancestors.join(" ")}
# Add indentation for the record names based on their nesting in the tree.
print_info = sorted_list.collect {|name, ancestors| (" " * (ancestors.size - 1) * INDENTATION_PER_LEVEL) + name}
print_info.each { |record| puts record }

Problem with multidimensional arrays in ruby [duplicate]

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.

ActiveRecord: filtering children without hitting the database

I'm looking for a clean way to filter the children of a parent in a has_many relationship without hitting the database and thus reloading the db's view of the objects back into the app.
For example:
class Parent < ActiveRecord::Base
has_many :children, ...
...
end
My understanding (correct me if I'm wrong) is that
parent.children.find_all_by_attr('foo')
returns all the parent's children that have a attr value of 'foo' in the db but because it hits the database again, any children that have had their attr values set to foo before being saved will have their db values restored thus overwriting any changes.
I've hacked around this with
parent.children.reject { |child| child.attr != 'foo' }
but this seems very sloppy and more difficult to read. Does anybody have a cleaner suggestion on how to do this?
After doing some poking around, it looks like it is a little more complicated than that.
My poking around went like this:
I created a pair of models, Parent
and Child with a has_many
relationship in a throwaway rails
app.
I opened up script/console and
poked around.
I created a new parent with a child and saved them
>> p = Parent.new;p.children << Child.new(:foo=>'bar');p.save
=> true
See the child is in the db and findable by_foo
>> p.children.find_by_foo('bar')
=> #<Child id: 1, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:05", updated_at: "2009-12-14 22:08:05">
I added another child to the collection,it shows up in p.children but not in collection methods that hit the db.
>> p.children << Child.new(:foo=>'bar')
=> [#<Child id: 1, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:05", updated_at: "2009-12-14 22:08:05">, #<Child id: 2, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:25", updated_at: "2009-12-14 22:08:25">]
>> p.children.find_by_foo('bar')
=> #<Child id: 1, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:05", updated_at: "2009-12-14 22:08:05">
I change the child that is in the db.
>> p.children[0].foo = 'baz'
=> "baz"
When I search for it, it gives me the db version.
>> p.children.find_by_foo('bar')
=> #<Child id: 1, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:05", updated_at: "2009-12-14 22:08:05">
But, the local collection is unchanged.
>> p.children
=> [#<Child id: 1, foo: "baz", parent_id: 1, created_at: "2009-12-14 22:08:05", updated_at: "2009-12-14 22:08:05">, #<Child id: 2, foo: "bar", parent_id: 1, created_at: "2009-12-14 22:08:25", updated_at: "2009-12-14 22:08:25">]
So if you save p again, it will pass on the changes.
If you want to get all the local association objects, including ones that have been changed, you can't use the ActiveRecord finders because they hit the db, instead use array methods like you did above. Though, using find_all or select would be easier to understand
parent.children.select{|c| c.attr == 'foo'}
You could do the opposite of reject, which is find_all:
parent.children.find_all {|child| child.attr == 'foo' }

Resources