Ruby: Defining attributes by looping an array? - ruby

I'm trying to define attributes from an array like so:
["a", "b", "c"].each do |field|
#recipe.field = "anything"
end
I want to end up with something like this:
#store.a = "anything"
#store.b = "anything"
#store.c = "anything"
Do you know what I should do with the #store.field above? I tried #store.send(field), but that is not working for me and I have no idea what keywords to search to find a solution to the above. Any help is greatly appreciated.

The setter method for attribute a is known as a=, so you can use send with an argument "a=" to call the setter method:
["a", "b", "c"].each do |field|
#recipe.send(field + "=", "anything")
end

If you want to dynamically add attributes to class, then you should use attr_accessor mthod (or check what it does
class Recipe
attr_accessor *["a", "b", "c"]
end
["a", "b", "c"].each do |field|
#recipe.send("#{field}=", "anything")
end
Edit:
As you see in example, if you want to assign something to field defined by def attr= method, then you need to call send with "attr=", value params.

Related

Ruby - how to pop a specific element from an array

What would be the easiest way in Ruby to pop a specific element from an array, similar to the .delete method of
a.delete(element)
rather than popping the first/last element or using .slice?
To make this more specific: for example, I can do
case names.sample when "John", "Dave", "Sam"
a.delete(names.sample)
end
to delete one of those names from a when it appears as a sample from names
However, I intend to use multiple samples and using a.delete()will remove all elements at once, rather than in succession like the result produced from shuffle!.pop where elements are popped in succession, so that the name can no longer be selected as a sample from a after the same name has been selected as a name.sample
I was wondering what the easiest way would be in Ruby to pop off these elements in succession, or if it is even possible at all in this context.
The Array class defines a pop method. It returns and deletes the last element in the array.
a = ["a", "b", "c"]
puts a.pop #=> "c"
puts a #=> ["a", "b"]
You can optionally pass an argument to pop that specifies how many elements to pop off.
a = ["a", "b", "c"]
puts a.pop(2) #=> ["b", "c"]
puts a #=> ["a"]
Addressing your last comment, you can use include?, index, and delete_at methods to achieve this. Assuming you're checking for "b" in an array:
a = ["a", "b", "c"]
value_index = a.index("b") #Returns the first occurring index of "b"
has_value = a.include?("b") #Returns whether "b" is in the list
a.delete_at(a.index("b")) if has_value #Removes "b" from the list
In this sample, "has_value" will be whether the a array contains the value "b", and "value_index" will be the first occurrence of "b". This will also delete the value "b" from the list.
If you want to remove all occurrences of "b", you can use include?, index, and delete_at with a while loop:
a = ["a", "b", "c", "a", "b", "c"]
while a.include?("b")
a.delete_at(a.index("b"))
end
#a will now be ["a", "c", "a", "c"]
See also the documentation for Array.
[..] intend to use multiple samples and using a.delete() will remove all elements at once, rather than in succession like the result produced from shuffle!.pop where elements are popped in succession, so that the name can no longer be selected as a sample from a after the same name has been selected as a name.sample[..]
Maybe you are looking something like this?
names = ["John", "Dave", "Sam"]
names.size.times { p names.delete(names.sample) }
#=> "Sam"
#=> "John"
#=> "Dave"

Why CONSTANT value in class is changed?

I have a class like this:
class Example
DEFAULT_VALUE = {
'first_key': ['a', 'b'],
'second_key': 'c'
}
def append_new_value(value)
default_value_copy = DEFAULT_VALUE
default_value_copy[:first_key] << value
puts "default_value_copy: #{default_value_copy}"
puts "DEFAULT_VALUE: #{DEFAULT_VALUE}"
end
end
example = Example.new
example.append_new_value('d')
example.append_new_value('e')
The results are:
default_value_copy: {:first_key=>["a", "b", "d"], :second_key=>"c"}
DEFAULT_VALUE: {:first_key=>["a", "b", "d"], :second_key=>"c"}
default_value_copy: {:first_key=>["a", "b", "d", "e"], :second_key=>"c"}
DEFAULT_VALUE: {:first_key=>["a", "b", "d", "e"], :second_key=>"c"}
As I understood before, the value of DEFAULT_VALUE should not be changed after calling append_new_value method.
Could you guys help me to explain about this case?
First of all, Ruby does not have the notion of constants like you would expect from other languages. Ruby constants can change values. If you want to specify that an object should not be mutated in Ruby you must use the Object#freeze method on it.
Then, there is the matter of Ruby passing method arguments by reference or by value. You could say that Ruby is pass-by-value in the traditional sense. However in Ruby all variables are references to objects, so if you pass an object to a method it will indeed get mutated.
If you're looking for quick wins in immutability, check out Object#dup.

What's the Ruby equivalent of map() for strings?

If you wanted to split a space-separated list of words, you would use
def words(text)
return text.split.map{|word| word.downcase}
end
similarly to Python's list comprehension:
words("get out of here")
which returns ["get", "out", "of", "here"]. How can I apply a block to every character in a string?
Use String#chars:
irb> "asdf".chars.map { |ch| ch.upcase }
=> ["A", "S", "D", "F"]
Are you looking for something like this?
class String
def map
size.times.with_object('') {|i,s| s << yield(self[i])}
end
end
"ABC".map {|c| c.downcase} #=> "abc"
"ABC".map(&:downcase) #=> "abc"
"abcdef".map {|c| (c.ord+1).chr} #=> "bcdefg"
"abcdef".map {|c| c*3} #=> "aaabbbcccdddeeefff"
I think the short answer to your question is "no, there's nothing like map for strings that operates a character at a time." Previous answerer had the cleanest solution in my book; simply create one by adding a function definition to the class.
BTW, there's also String#each_char which is an iterator across each character of a string. In this case String#chars gets you the same result because it returns an Array which also responds to each (or map), but I guess there may be cases where the distinction would be important.

Is there analogue for this ruby method?

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.

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(', ')

Resources