Ruby: add a simple function to Mylist < Array - ruby

I'm struggling with some database migration stuff, and this is what I want to do:
I created a Class Mylist < Array with a method each_hash added, this method needs to act just like the Array's each, that means the two calls are equal:
list = Mylist.new
list.each |d|
...
end
list.each_hash |d|
...
end
I have tried here and there but it does not work, any help would be appreciated!

Thanks #Charles Caldwell, works excellent
If you want the each_hash to be the exact same thing as each, you could do alias :each_hash :each. It would make a call to each_hash to actually be a call to each. Would that be an option in your case? – Charles Caldwell

Related

Why can't I get Array#slice to work the way I want?

I have a big Array of AR model instances. Let's say there are 20K entries in the array. I want to move through that array a chunk of 1,000 at a time.
slice_size = 1000
start = 0
myarray.slice(start, slice_size) do |slice|
slice.each do |item|
item.dostuff
end
start+=slice_size
end
I can replace that whole inner block with just:
puts "hey"
and not see a thing in the console. I have tried this 9 ways from Sunday. And I've done it successfully before, just can't remember where. And I have RTFM. Can anyone help?
The problem is that slice does not take a block, but you are passing it a block, and trying to do something in it, which is ignored. If you do
myarray.slice(start, slice_size).each do |slice|
...
end
then it should work.
But to do it that way is not Ruby-ish. A better way is
myarray.each_slice(slice_size) do |slice|
...
end
If the array can be destroyed, you could do it like this:
((myarray.size+slice_size-1)/slice_size).times.map {myarray.shift(slice_size)}
If not:
((myarray.size+slice_size-1)/slice_size).times.map { |i|
myarray.slice(i*slice_size, slice_size) }
You can use:
Enumerable#each_slice(n) which takes n items at a time;
Array#in_groups_of(n) (if this is Rails) which works like each_slice but will pad the last group to guarantee the group size remains constant;
But I recommend using ActiveRecord's built-in Model.find_each which will batch queries in the DB layer for better performance. It defaults to 1000, but you can specify the batch size. See http://guides.rubyonrails.org/active_record_querying.html#retrieving-multiple-objects-in-batches for more detail.
Example from the guide:
User.find_each(batch_size: 5000) do |user|
NewsLetter.weekly_deliver(user)
end

returning array without specific elements in ruby

I looked really hard in http://www.ruby-doc.org/core-2.1.2/Array.html but I couldn't find a quick functionality to this behaviour:
arr = [1,2,3,4,5,6]
arr.without(3,6) #=> [1,2,4,5]
I know I can write my own function/monkey-patch ruby/add a class method/write it in a few lines.
Is there a way to do this in a ruby way?
you can use subtraction :
arr - [3,6]
EDIT
if you really wanted you could alias this method
class Array
alias except -
end
then you can use:
arr.except [3,6]
This got added in Rails 5 :)
https://github.com/rails/rails/issues/19082
module Enumerable
def without(*elements)
reject { |element| element.in?(elements) }
end
end
it's just aesthetics, but it makes sense for the brain
There is another way using reject. But it is not cleaner than -
arr.reject{|x| [3,6].include? x}
Just in case anyone else comes across this and is looking for a way to delete elements from an array based on a conditional: you can use delete_if.
For example, I have a list of customers and want to merge any customers that have duplicate emails. After doing a query to get a list of all emails with the total count number for each email, I can then delete all of them that only appear once:
emails = Customer.select('count(email) as num_emails, email').group('email')
emails.delete_if { |email| email.num_emails.to_i == 1 }
The end result is I have a list of all customer emails that appear multiple times in the database.

Ruby method chaining - `tap` with replacement [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is there a `pipe` equivalent in ruby?
I'm looking at the tap method in Ruby - but unfortunately the object returned from the passed block is not passed on. What method do I use to have the object passed on?
Here's what I'm trying to (unsuccessfully) do:
obj.tap{ |o| first_transform(o) }.tap{ |o| second_transform(o)}
This is, of course, equivalent to second_transform(first_transform(o)). I'm just asking how to do it the first way.
Doing this is trivial with lists:
list.map{ |item| first_transform(item) }.map{ |item| second_transform(item)}
Why isn't it as easy with objects?
class Object
def as
yield self
end
end
With this, you can do [1,2,3].as{|l| l << 4}.as{|l| l << 5}
You could also consider to make #first_transform and #second_transform instance methods of the item's class (and return the transformed item of course).
These methods definitions should look like this:
class ItemClass
# If you want your method to modify the object you should
# add a bang at the end of the method name: first_transform!
def first_transform
# ... Manipulate the item (self) then return it
transformed_item
end
end
This way you could simply chain the methods calls this way:
list.map {|item| item.first_transform.second_transform }
It even reads better in my humble opinion ;)
The simple answer is tap doesn't do what you think it does.
tap is called on an object and will always return that same object.
As a simple example of taps use
"foo".tap do |foo|
bar(foo)
end
This still returns "foo"
In your example you have an object, and you want to apply two functions to it in succession.
second_transform(first_transform(obj))
UPDATED:
So I guess I'd ask why you want to chain in this way.
obj.do{|o| first_transform(o)}.do{|o| second_transform(o)}
Is that really more clear than
second_transform(first_transform(obj))
Lets take an example I often use
markdown(truncate(#post.content))
or
truncated_post = truncate(#post.content)
markdown(truncated_post)
I guess it depends on the nature of your transform

Add element to an array if it's not there already

I have a Ruby class
class MyClass
attr_writer :item1, :item2
end
my_array = get_array_of_my_class() #my_array is an array of MyClass
unique_array_of_item1 = []
I want to push MyClass#item1 to unique_array_of_item1, but only if unique_array_of_item1 doesn't contain that item1 yet. There is a simple solution I know: just iterate through my_array and check if unique_array_of_item1 already contains the current item1 or not.
Is there any more efficient solution?
#Coorasse has a good answer, though it should be:
my_array | [item]
And to update my_array in place:
my_array |= [item]
You can use Set instead of Array.
You don't need to iterate through my_array by hand.
my_array.push(item1) unless my_array.include?(item1)
Edit:
As Tombart points out in his comment, using Array#include? is not very efficient. I'd say the performance impact is negligible for small Arrays, but you might want to go with Set for bigger ones.
You can convert item1 to array and join them:
my_array | [item1]
Important to keep in mind that the Set class and the | method (also called "Set Union") will yield an array of unique elements, which is great if you want no duplicates but which will be an unpleasant surprise if you have non-unique elements in your original array by design.
If you have at least one duplicate element in your original array that you don't want to lose, iterating through the array with an early return is worst-case O(n), which isn't too bad in the grand scheme of things.
class Array
def add_if_unique element
return self if include? element
push element
end
end
I'm not sure if it's perfect solution, but worked for me:
host_group = Array.new if not host_group.kind_of?(Array)
host_group.push(host)

How to improve the way I browse an array with .each while trying to keep track of the index ("i") using Ruby?

Let's say I'm doing a simple .each but I still want to keep the position in the loop, I can do:
i = 0
poneys.each do |poney|
#something involving i
#something involving poney
i = i + 1
end
This doesn't look very elegant to me. So I guess I could get rid of the .each:
for i in 0..poneys.size-1 do
#something involving i
end
... or something similar with a different syntax.
The problem is that if I want to access the object I have to do:
for i in 0..poneys.size-1 do
poney = poneys[i]
#something involving i
#something involving poney
end
... and that's not very elegant either.
Is there a nice and clean way of doing this ?
You can use Enumerable#each_with_index
From the official documentation:
Calls block with two arguments, the
item and its index, for each item in
enum.
hash = Hash.new
%w(cat dog wombat).each_with_index do |item, index|
hash[item] = index
end
hash #=> {"cat"=>0, "wombat"=>2, "dog"=>1}
Depends what do you do with poneys :) Enumerable#inject is also a nice one for such things:
poneys.inject(0) do |i, poney|
i += 1; i
end
I learned a lot about inject from http://blog.jayfields.com/2008/03/ruby-inject.html which is great article.

Resources