Is there analogue for this ruby method? - ruby

Recently I've came up with this method:
module Enumerable
def transform
yield self
end
end
The purpose of method is similar to tap method but with the ability to modify object.
For example with this method I can change order in an array in chain style:
array.do_something.transform{ |a| [a[3],a[0],a[1],a[2]] }.do_something_else
Instead of doing this:
a0,a1,a2,a3 = array.do_something
result = [a3, a0, a1, a2].do_something_else
There are also another conveniences when using this method but...
The method is very straightforward, so I guess somewhere should be the already built method with the same purpose.
Is there analogue for this ruby method?

You can do that with instance_eval:
Evaluates (…) the given block, within the context of the receiver
Example:
%w(a b c d).instance_eval{|a| [a[3], a[0], a[1], a[2]] }
# => ["d", "a", "b", "c"]
or using self:
%w(a b c d).instance_eval{ [self[3], self[0], self[1], self[2]] }
# => ["d", "a", "b", "c"]

I can't test this now but you should be able to do something like this:
array= [1,2,3]
array.tap{ |a| a.clear }
Tap runs the block then returns self so if you can modify self in the block, it will pass back the updated array. In my example clear modifies self in the block so the modified self is returned.
If you want this functionality, I would suggest adding a method like do_something_else! that modifies self then running it within your tap block.

So where is the question? It all depends on how far you want to go into functional programming realm. Just read Learn You a Haskell for Great Good! and you will never be the same. Once you open this Pandora box it's really hard to stop and after some experimenting I wonder if I'm still writing Ruby code. Compare (using your transform method defined for Object)
h = {}
{:a => :a_method, :b => :b_method}.each do |k, m|
h[k] = some_object.__send__(m)
end
h.some_other_method
and
some_object.transform(&THash[:a => :a_method, :b => :b_method]).some_other_method
where
THash =
lambda do |t|
h = {}
kms = t.map { |k, v| [k, v.to_proc] }
lambda do |x|
kms.each { |k, m| h[k] = m[x] }
end
end
So if you want to think of your objects in terms of transformations, it makes perfect sense and does make code more readable, but it's more than just transform method, you need to define generic transformations you use frequently.
Basically it's called point-free programming, though some call it pointless. Depends on your mindset.

Related

Need Ruby method to convert an array of strings into a Hash

I need Ruby method to convert an array of strings into a Hash where each key is a string and each value is the 1-indexed index of the string in the original array.
hashify(%w(a b c))
# should return
{'a' => 1, 'b' => 2, 'c' => 3}
Even though I think I'm helping someone do their homework, I can't resist taking a golf swing, because Ruby is awesome:
%w(a b c).each.with_index(1).to_h
Also, defining "hashify" is VERY un-Ruby-like. I'd suggest alternatives, but it's probably a homework assignment anyways and you don't seem to want to learn it.
def hashify(array)
array.each.with_index(1).to_h
end
hashify(%w(a b c))
#=> { "a" => 1, "b" => 2, "c" => 3 }
There are (clearly) multiple ways you could achieve your goal in Ruby.
If you consider the expression %w(a b c).map.with_index(1).to_h, you can see that it is a matter of stringing together a few methods provided to us by the Enumerable class.
By first sending the :map message to the array, we receive back an Enumerator, which provides us the handy :with_index method. As you can see in the docs, with_index accepts an offset in an argument, so offsetting your indices by 1 is as simple as passing 1 as your argument to :with_index.
Finally, we call :to_h on the enumerator to receive the desired hash.
# fore!
def hashify(array)
array.map.with_index(1).to_h
end
> hashify %w(a b c)
=> {"a"=>1, "b"=>2, "c"=>3}
Try this, this will add a method to_hash_one_indexed to the Array class.
class Array
def to_hash_one_indexed
map.with_index(1).to_h
end
end
Then to call it:
%w(a b c).to_hash_one_indexed
#=> {"a"=>1, "b"=>2, "c"=>3}

Ruby: cleaner returns from loop iteration methods

I find that I frequently have methods that iterate through an enumerable in order to return a different enumerable or a hash. These methods almost always look like this simplistic example:
def build_hash(array)
hash = {}
array.each do |item|
hash[ item[:id] ]= item
end
hash
end
This approach works works, but I've often wondered if there's a cleaner way to do this, specifically without having to wrap the loop in a temporary object so that the return is correct.
Does anyone know of an improved and/or cleaner and/or faster way to do this, or is this pretty much the best way?
Here are a few ways, considering your specific example
arr = [{:id => 1, :name => :foo}, {:id => 2, :name => :bar}]
Hash[arr.map{ |o| [o[:id], o] }]
arr.each_with_object({}){ |o, h| h[o[:id]] = o }
arr.reduce({}){ |h, o| h[o[:id]] = o; h }
arr.reduce({}){ |h, o| h.merge o[:id] => o }
# each of these return the same Hash
# {1=>{:id=>1, :name=>:foo}, 2=>{:id=>2, :name=>:bar}}
Well in this case, you can use inject and do something like this :
def build_hash(array)
array.inject({}) { |init, item| init[item[:id]] = item; init }
end
{}.tap { |h| array.each { |a| h[a[:id]] = a } }
Here is also a way how to convert Array into Hash.
list_items = ["1", "Foo", "2", "Bar", "3" , "Baz"]
hss = Hash[*list_items]
parameters must be even, otherwise a fatal error is raised, because an odd
number of arguments can’t be mapped to a series of key/value pairs.
{"1"=>"Foo", "2"=>"Bar", "3"=>"Baz"}
You can use ActiveSupport's index_by.
Your example becomes trivial:
def build_hash(array)
array.index_by{|item| item[:id]}
end
There is no really great way to build a hash in Ruby currently, even in Ruby 2.0.
You can use Hash[], although I find that very ugly:
def build_hash(array)
Hash[array.map{|item| [item[:id], item]}]
end
If we can convince Matz, you could at least:
def build_hash(array)
array.map{|item| [item[:id], item]}.to_h
end
There are other requests for new ways to create hashes.

Bidirectional Hash table in Ruby

I need a bidirectional Hash table in Ruby. For example:
h = {:abc => 123, :xyz => 789, :qaz => 789, :wsx => [888, 999]}
h.fetch(:xyz) # => 789
h.rfetch(123) # => abc
h.rfetch(789) # => [:xyz, :qaz]
h.rfetch(888) # => :wsx
Method rfetch means reversed fetch and is only my proposal.
Note three things:
If multiple keys map at the same value then rfetch returns all of them, packed in array.
If value is an array then rfetch looks for its param among elements of the array.
Bidirectional Hash means that both fetch and rfetch should execute in constant time.
Does such structure exists in Ruby (including external libraries)?
I thought about implementing it using two one-directional Hashes synchronized when one of them is modified (and packing it into class to avoid synchronization problems) but maybe I could use an already existing solution?
You could build something yourself pretty easily, just use a simple object that wraps two hashes (one for the forward direction, one for the reverse). For example:
class BiHash
def initialize
#forward = Hash.new { |h, k| h[k] = [ ] }
#reverse = Hash.new { |h, k| h[k] = [ ] }
end
def insert(k, v)
#forward[k].push(v)
#reverse[v].push(k)
v
end
def fetch(k)
fetch_from(#forward, k)
end
def rfetch(v)
fetch_from(#reverse, v)
end
protected
def fetch_from(h, k)
return nil if(!h.has_key?(k))
v = h[k]
v.length == 1 ? v.first : v.dup
end
end
Look ups will behave just like normal hash lookups (because they are normal hash lookups). Add some operators and maybe decent to_s and inspect implementations and you're good.
Such a thing works like this:
b = BiHash.new
b.insert(:a, 'a')
b.insert(:a, 'b')
b.insert(:a, 'c')
b.insert(:b, 'a')
b.insert(:c, 'x')
puts b.fetch(:a).inspect # ["a", "b", "c"]
puts b.fetch(:b).inspect # "a"
puts b.rfetch('a').inspect # [:a, :b]
puts b.rfetch('x').inspect # :c
puts b.fetch(:not_there).inspect # nil
puts b.rfetch('not there').inspect # nil
There's nothing wrong with building your tools when you need them.
There is no such structure built-in in Ruby.
Note that Hash#rassoc does something similar, but it returns only the first match and is linear-time:
h = {:abc => 123, :xyz => 789, :qaz => 789, :wsx => [888, 999]}
h.rassoc(123) # => [:abc, 123]
Also, it isn't possible to fullfill your requirements in Ruby in a perfectly safe manner, as you won't be able to detect changes in values that are arrays. E.g.:
h = MyBidirectionalArray.new(:foo => 42, :bar => [:hello, :world])
h.rfetch(:world) # => :bar
h[:bar].shift
h[:bar] # => [:world]
h.rfetch(:world) # => should be nil, but how to detect this??
Computing a hash everytime to detect a change will make your lookup linear-time. You could duplicate the array-values and freeze them, though (like Ruby does for Hash keys that are strings!)
What you seem to need is a Graph class, which could have a different API than a Hash, no? You can check out rgl or similar, but I don't know how they're implemented.
Good luck.
There is a Hash#invert method (http://www.ruby-doc.org/core-2.1.0/Hash.html#method-i-invert) to achieve this. It won't map multiple values to an array though.
Try this:
class Hash
def rfetch val
select { |k,v| v.is_a?(Array) ? v.include?(val) : v == val }.map { |x| x[0] }
end
end
If you're not doing lots of updates to this hash, you might be able to use inverthash.

Ruby Hash/Array delete_if without a block

According to Ruby Hash/Array documentation, the delete_if method returns an enumerator if no block is given. How is this useful? Can someone give an example to demonstrate this pattern?
There are some methods defined on Enumerator that give flexibility to iterators. One such method I often use is with_index.
p %w[a b c d e f].delete_if.with_index{|_, i| i.even?}
# => ["b", "d", "f"]
If this was to be done without Enumerator class, all kinds of methods have to be defined, including delete_if_with_index, and that is not a good thing.
The enumerator will just allow you to run the block later. For example, if you had a method that specifically handled the delete if for several different objects, you could pass it the enumerator.
In the example below, it will print 1, 3, 5
arr = [0,1,2,3,4,5]
enumerator = arr.delete_if
enumerator.each { |el| el.even? }
puts arr.join(', ')

How To keep track of counter variables in ruby, block, for, each, do

I forget how to keep track of the position of the loops in Ruby. Usually I write in JavaScript, AS3, Java, etc.
each:
counter = 0
Word.each do |word,x|
counter += 1
#do stuff
end
for:
same thing
while:
same thing
block
Word.each {|w,x| }
This one I really don't know about.
In addition to Ruby 1.8's Array#each_with_index method, many enumerating methods in Ruby 1.9 return an Enumerator when called without a block; you can then call the with_index method to have the enumerator also pass along the index:
irb(main):001:0> a = *('a'..'g')
#=> ["a", "b", "c", "d", "e", "f", "g"]
irb(main):002:0> a.map
#=> #<Enumerator:0x28bfbc0>
irb(main):003:0> a.select
#=> #<Enumerator:0x28cfbe0>
irb(main):004:0> a.select.with_index{ |c,i| i%2==0 }
#=> ["a", "c", "e", "g"]
irb(main):005:0> Hash[ a.map.with_index{ |c,i| [c,i] } ]
#=> {"a"=>0, "b"=>1, "c"=>2, "d"=>3, "e"=>4, "f"=>5, "g"=>6}
If you want map.with_index or select.with_index (or the like) under Ruby 1.8.x, you can either do this boring-but-fast method:
i = 0
a.select do |c|
result = i%2==0
i += 1
result
end
or you can have more functional fun:
a.zip( (0...a.length).to_a ).select do |c,i|
i%2 == 0
end.map{ |c,i| c }
If you use each_with_index instead of each, you'll get an index along with the element. So you can do:
Word.each_with_index do |(word,x), counter|
#do stuff
end
For while loops you'll still have to keep track of the counter yourself.
A capital W would mean it's a constant which most likely mean it's a class or a module not an instance of a class. I guess you could have a class return an enumerable using each but that seems very bizarre.
To remove the confusing extra junk and the, possibly, incorrectly capitalized example I would make my code look like this.
words = get_some_words()
words.each_with_index do |word, index|
puts "word[#{index}] = #{word}"
end
I'm not sure what Sepp2K was doing with the weird (word,x) thing.

Resources