Difference between array<<element and array.push(element)? or string<<"something" and string+"something"? in Ruby - arrays

Before judging me for an irrelevant question, I'll safeguard myself, that I know << is a bitwise operator. However, in both cases (array, string) it operates as just adding / concatenating values.
Any tip for clarifying whether there's difference if we use array<
Thanks

However, in both cases (array, string) it operates as just adding /
concatenating values.
It makes no difference "result-wise" - in both cases you get a value containing both operands (in that or another form).
The difference shows up in the way operands are impacted:
one performs in-place mutating
second is simply concatenating without changing the original strings.
Consider the following examples:
a = 'a'
b = 'b'
Now:
# concatenation - no changes to original strings
a + b #=> "ab"
a #=> "a"
b #=> "b"
Whereas:
# mutation - original string is changed in-place
a << b #=> "ab"
a #=> "ab"
Same goes with arrays:
# concatenation - no changes to original arrays
a = ['a'] #=> ["a"]
b = ['b'] #=> ["b"]
a + b #=> ["a", "b"]
a #=> ["a"]
b #=> ["b"]
# mutation - original array is changed in-place
a << b #=> ["a", ["b"]]
a #=> ["a", ["b"]]
As to Array#push and Array#<< - they do the same thing.

Firstly, << is not a bit-wise operator. Nor is :<< or "<<". The first is not a Ruby object or keyword. :<< is a symbol and "<<" is a string. Fixnum#<<, by constrast, is a bit-wise operator, implemented as an instance method on the class Fixnum.
You may argue that it's obvious what you meant, but it's not. Many classes have instance methods of the same name that are unrelated. Several classes, for example, have methods called "<<", "+", "size", "replace", "select", "each" and on and on. The only way to speak meaningfully of an instance method, therefore, is to also give the class on which it is defined.
What is an "operator" in Ruby? Frankly, I don't know. I've never found a definition. Whatever it is, however, most of them are implemented as instance methods.
Many of Ruby's core methods have names that may seem unusual to those coming from other languages. Examples are "<<", "+" and "&". The important thing to remember is that these are perfectly-valid names. Let's try using them as you would any other method:
[1,2,3].<<(4) #=> [1, 2, 3, 4]
"cat".+("hat") #=> "cathat"
[1,2,3].&([2,4]) #=> [2]
The head Ruby monk knew that his disciples would prefer to write these as follows:
[1,2,3] << 4
"cat" + "hat"
[1,2,3] & [2,4]
so he said "OK", which when translated from Japanese to English means "OK". He simply designed the Ruby parser so that when it saw the later form it would convert the expression to the standard form before parsing it further (or something like that). This has come to be called syntactic sugar. (Syntactic sugar doesn't allow you to write "cat" concat "hat", however--it only applies to names that are made up of symbols.)
My point with Ruby's operators is that most are implement with garden-variety methods, albeit methods with odd-sounding names. Yes, there are methods String#+ and Array#+ but they are completely unrelated to each other. If they were instead named String#str_add and Array#arr_add and used like so:
"abc".str_add("def")
[1,2,3].arr_add([2,4])
you probably wouldn't be asking the question you've raised.

Related

Sorting an Array with Sort and Sort_by

I am trying to build a mental model around how the code below works and it is confusing me. I am trying to sort a string so every duplicate letter is together, but the capital letter comes first. It is solved, the method below will do it, but I want to know why do you have to sort it first? Does it keep the same position from the first sort? So when you call sort_by it then sorts by lowercase but the capital letters stay where they originally were? Can anyone break down step by step what is happening so I can understand this better?
def alpha(str)
str.chars.sort.sort_by { |ele| ele.downcase }.join
end
alpha("AaaaaZazzz") == "AaaaaaZzzz"
Let's rewrite your method as follows.
def alpha(str)
sorted_chars_by_case = str.chars.sort
puts "sorted_chars = #{sorted_chars}"
sorted_chars_by_downcased = sorted_chars_by_case.sort_by(&:downcase)
puts "sorted_chars_by_downcased = #{sorted_chars_by_downcased}"
sorted_chars_by_downcased.join
end
Then:
alpha("AaaaaZazzz")
sorted_chars_by_case = ["A", "Z", "a", "a", "a", "a", "a", "z", "z", "z"]
sorted_chars_indifferent = ["A", "a", "a", "a", "a", "a", "Z", "z", "z", "z"]
#=> "AaaaaaZzzz"
As you see, the first step, after converting the string to an array of characters, is to form an array whose first elements are upper-case letters, in order, followed by lower-case letters, also ordered.1 The second step is sort sorted_chars_by_case without reference to case. That array is then joined to return the desired string, "AaaaaaZzzz".
While this gives the desired result, it is only happenstance that it does. A different sorting method could well have returned, say, "aaaaAazZzz", because "A" is treated the same as "a" in the second sort.
What you want is a two-level sort; sort by characters, case-indifferent, then when there are ties ("A" and "a", for example), sort the upper-case letter first. You can do that by sorting two-element arrays.
def alpha(str)
str.each_char.sort_by { |ele| [ele.downcase, ele] }.join
end
Then
alpha("AaaaaZazzz")
#=> "AaaaaaZzzz"
When sorting arrays the method Array#<=> is used to order two arrays. Note in particular the third paragraph of that doc.
If "A" and "z" are being ordered, for example, Ruby compares the arrays
a1 = ["a", "A"]
a2 = ["z", "z"]
As a1.first < a2.first #=> true, we see that a1 <=> a2 #=> -1, so "A" precedes "z" in the sort. Here a1.last and a2.last are not examined.
Now suppose "z" and "Z" are being ordered. Ruby compares the arrays
a1 = ["z", "z"]
a2 = ["z", "Z"]
As a1.first equals a2.first, a1.last and a2.last are compared to break the tie. Since "z" > "Z" #=> true, a1 <=> a2 #=> 1, so "Z" precedes "z" in the sort.
Note that I replace str.chars with str.each_char. It's generally a small thing, but String#chars returns an array of characters, whereas String#each_char returns an enumerator, and therefore is more space-efficient.
Sometimes you need to return an array, and therefore you must use chars. An example is str.chars.cycle, where you are chaining to the Array method cycle. On the other hand, if you are chaining to an enumerator (an instance of the class Enumerator), you must use each_char, an example being str.each_char.with_object([]) ....
Often, however, you have a choice: str.chars.sort, using Array#sort, or str.each_char.sort, using Enumerable#sort. In those situations each_char is preferred because of the reduced memory requirement. The rule, therefore, is to use chars when you are chaining to an Array method, otherwise use each_char.
1. sort_by(&:downcase) can be thought of as shorthand for sort_by { |ele| ele.downcase }.
You can't depend on the stability of sort in Ruby
This is an interesting question. Whether or not a sort preserves the order of equal elements is its "stability." A sort is stable if it is guaranteed to preserve the order of equal elements, and unstable if it has no such guarantee. An unstable sort may by chance return equal elements in their original order, or not.
In MRI 2.7.1, sort happens to be stable, but it is actually implementation defined whether or not it is. See https://stackoverflow.com/a/44486562/238886 for all the juicy details, including code you can run in your Ruby to find out if your sort happens to be stable. But whether or not your sort is stable, you should not depend on it.
A stable sort does indeed return the result you are expecting, and it does so whether or not you include the .sort:
2.7.1 :035 > "AaaaaZazzz".chars.sort_by { |ele| ele.downcase }.join
=> "AaaaaaZzzz"
2.7.1 :036 > "AaaaaZazzz".chars.sort.sort_by { |ele| ele.downcase }.join
=> "AaaaaaZzzz"
But you can make sort act stable when you need
In order to not depend up on the stability of the sort, which could change when you move your code to another Ruby version or implementation, you can enforce stability like this:
"AaaaaZazzz".chars.sort_by.with_index { |ele, i| [ele.downcase, i] }.join
=> "AaaaaaZzzz"
How does unstable sort behave
We can force Ruby 2.7.1's sort to be unstable by adding a random number as a secondary sort order:
2.7.1 :040 > "AaaaaZazzz".chars.sort.sort_by { |ele, i| [ele.downcase, rand] }.join
=> "AaaaaaZzzz"
2.7.1 :041 > "AaaaaZazzz".chars.sort.sort_by { |ele, i| [ele.downcase, rand] }.join
=> "aaaaAazzZz"
Note how we got the same answer as stable sort the first time, but then a different answer? That's a demonstration of how an unstable sort can, by chance, give you the same results as a stable sort. But you can't count on it.
First you sort in this code all characters alphabetically based on the collating sequence of the underlying encoding, and then you sort the characters in a way that upper and lower case characters are treated equivalent. This cancels the effect of the first sort. Hence the output is equivalent to str.chars.sort_by(&:downcase), which would IMO a more sensible way to write the expression.
The first sort has no effect and is therefore just a cycle stealer. BTW: Since the stability of Ruby sort is unspecified, and in particular MRI Ruby is known to be unstable, you have no control about the relative order of individual characters which are considered equivalent in sort order. Note also that the result depends on the locale, because this decides whether - for instance - the letters Б and б are considered the same in sort order or different.

How do I change the functionality of the built in Array class in Ruby?

I am writing code in Ruby.
Say I have an array like this:
myarray = [1,2,3,4,5]
I want to change the code of the Array class so that myarray[7] would return "out of bounds" instead of nil.
I don't want to make so that I check if something is out of bounds and then return "out of bounds", I want to implement this in Array directly.
Is this possible? Do I have to extend the Array class?
I'm Not sure how to start, very new to ruby. My initial thoughts were overriding a method in the Array class, but I'm not sure how to do that when I don't know what method is being used in myarray[i].
Suppose
arr = [1,2,3,4,5]
Let's start by examining the doc Array#[]. As seen, that method can have a single integer argument:
arr[2] #=> 3
arr[-2] #=> 4
arr[5] #=> nil
arr[-7] #=> nil
arr['x'] #=> TypeError (no implicit conversion of String into Integer)
or it may have two integer arguments:
arr[1,3] #=> [2, 3, 4]
arr[-3,2] #=> [3, 4]
arr[3,5] #=> [4, 5]
arr[7,4] #=> nil
arr[2,'x'] #=> TypeError (no implicit conversion of String into Integer)
or it may have a range of integers as an argument:
arr[1..3] #=> [2, 3, 4]
arr[1...3] #=> [2, 3]
arr[-5..-2] #=> [1, 2, 3, 4]
arr[3..9] #=> [4, 5]
arr[7..10] #=> nil
arr[1..'x'] #=> ArgumentError (bad value for range)
As I understand, you wish to alter Array#[] in such way that its behavior is unchanged except for the case when its argument is a single integer representing an offset into the array that is out-of-bounds.1. That could be done by writing the method in a module and then prepend that module to the class Array with the method Module#prepend.
module NewSlice
def [](*args)
arg1, arg2 = args
if arg2.nil? && arg1.is_a?(Integer) &&
(arg1 > self.size-1 || arg1 < -self.size)
"out of range"
else
super
end
end
end
Array.prepend(NewSlice)
arr[2] #=> 3
arr[-2] #=> 4
arr[5] #=> "out of range"
arr[-7] #=> "out of range"
arr['x'] #=> TypeError (no implicit conversion of String into Integer)
arr[1,3] #=> [2, 3, 4]
arr[-3,2] #=> [3, 4]
arr[3,5] #=> [4, 5]
arr[7,4] #=> nil
arr[2,'x'] #=> TypeError (no implicit conversion of String into Integer)
arr[1..3] #=> [2, 3, 4]
arr[1...3] #=> [2, 3]
arr[-5..-2] #=> [1, 2, 3, 4]
arr[3..9] #=> [4, 5]
arr[7..10] #=> nil
arr[1..'x'] #=> ArgumentError (bad value for range)
We see that
Array.ancestors
#=> [NewSlice, Array, Enumerable, Object, Kernel, BasicObject]
showing that super in NewSlice#[] calls the original method Array#[] after NewSlice has been prepended to Array. Since super has no arguments all the arguments that were passed to the new Array#[] method ( defined in NewSlice) are passed to the original Array#[].
Aliases were commonly used in Ruby prior to the introduction of Module#prepend in Ruby v2.0. They are still used but their importance has diminished greatly since prepend has become available.
If nil is to be replaced by "out of bounds" in all cases where Array#[] returns nil, write
module NewSlice
def [](*args)
super || "out of bounds"
end
end
Array.prepend(NewSlice)
arr[5] #=> "out of range"
arr[-7] #=> "out of range"
arr[7,4] #=> "out of range"
arr[7..10] #=> "out of range"
1. Whether it is advisable to modify core methods such as Array#[] is a separate question, but it can result in pain and suffering.
TL;DR
This is an interesting question about how to monkeypatch core Ruby classes like Array. However, I'd be remiss if I didn't point out that it involves some hijinks that seem unnecessary, since you could get the same behavior with a simple logical OR operator like:
[1, 2, 3][5] || 'out of bounds'
#=> "out of bounds"
Still, if you want to change the behavior of a core class then it can certainly be done.
Re-Open the Core Class
In Ruby, almost everything that isn't a keyword is a method. Pragmatically, Array::[] is really just a method for indexing into an array object, with a little syntactic sugar from the interpreter to allow its arguments to be placed inside the brackets. Because it's just an instance method defined by the Array class, it can be modified in various ways such as re-opening the core class to modify the method, or by creating a singleton method on the instance. The first is conceptually simpler, but singleton methods are probably the safest solution for real-world programming.
You can re-open the Array class and redefine Array::[] as follows:
class Array
# save off a copy of the existing method; we still need it
alias_method :'old_[]', :'[]' unless defined? Array::old_[]
# pass all arguments to the old method, but return a string
# when the result is falsey
def [] *args
send("old_[]", *args) || 'out of bounds'
end
end
You can then demonstrate that it works like a regular Array, but with your expected behavior:
a = [10, 20, 30]
a[0] #=> 10
a[0,2] #=> [10, 20]
a[100] #=> "out of bounds"
Add a Singleton Method
While the code above works, it's just generally a bad idea to monkeypatch a core class like Array. If you don't want to encapsulate this behavior in a subclass (e.g. class MyArray < Array), then you should use refinements (using Module#refine) or singleton methods like the following to reduce the scope of your changes. For example:
a = [10, 20, 30]
class << a
alias_method :'old_[]', :'[]' unless self.respond_to? 'old_[]'
def [] *args
send("old_[]", *args) || 'out of bounds'
end
end
By limiting the scope of our change to a single instance, we're much less likely to cause serious or unrecoverable problems with core classes. A lot of things can go wrong in a Ruby program if a core class like Array starts returning truthy values (e.g. a String value such as out of bounds) rather than a falsey value like nil, so caveat emptor!
If you really want to break existing code (standard classes and ruin even methods of the current Array class itself, which rely on the current behaviour), you can of course monkeypatch it - open it and redefine the method in any way you like, as has been outlined in various answers to this question.
A more sane approach would be to subclass Array, i.e.
class BoundCheckingArray < Array
# redefined the methods according to your taste.
end
Of course this would violate the Liskov Substitution Principle.
Another approach will be to define a complete separate class and use Delegator to avoid rewriting everything from scratch, by delegating methods you did not rewrite explicitly, to the underlying array.
In both cases you need to be aware, that those methods in your BoundCheckingArray, which you did not explicitly rewrite and hence are taken from BoundCheckingArray, will stop working, if they assume that they can safely access the array outside of the bounds.
Another possibility would be to not modify existing Array methods, but simply add bound checking setters and getters, i.e. with monkeypatching:
class Array
def at_checked(i)
fail "out_of_bounds" if i >= size || i < -size
at(i)
end
def set_checked(i, new_value)
# analoguous
end
end
Of course this approach would also work with subclassing (now adhering to Liskov's principle, because you just added methods) and delegating. Note that I coped explicitly with negative index values, assuming that you would like to use this feature in your bound checking array too.
You may know classes in Ruby are defined like:
class Foo
def foo
'foo'
end
end
Foo.new.foo # calls instance method `foo` on the instance of `Foo`
# => 'foo'
Classes and modules in Ruby can be "reopened" for re-definition (although it's a bit frowned upon -- makes it difficult to track code b/c code can be in either/any place):
class Foo
def bar
'bar'
end
def foo
'overwrite foo'
end
end
f = Foo.new
f.bar
# => 'bar'
f.foo
# => 'overwrite foo'
This means you can also do:
class Array
# re-define anything you want
end
# alternatively:
Array.class_eval do
# the same as the above, in most ways
end
Even though it's even less recommended to modify existing methods of core Ruby classes. Not only does it break assumptions other Ruby developers may have of the code (i.e. misleading code), its global effect will likely break behaviors in other libraries, etc.

Array method versus hashes

I have an object (returned from and API) that is either a hash or an array of hashes. I want to enclose it in an array if it is not already an array.
I tried to apply Array on it, which functions in an expected way with numbers or arrays:
Array(1) # => [1]
Array([1, 2]) # => [1, 2]
Array([{a: 1}, {b: 2}]) # => [{:a=>1}, {:b=>2}]
but it fails with hashes:
Array({a: 1}) # => [[:a, 1]]
which should be [{:a=>1}].
Alternatively, I could add a type check:
responses = [responses] if responses.is_a?(Hash)
Is there a better solution?
ActiveSupport introduces Array#wrap that does exactly what you want:
Array.wrap(responses)
I personally prefer to never use any Rails helpers for many reasons, so I would stick with [responses].flatten, or, even better, with the most explicit version:
case responses
when Hash then [responses]
when Array then responses
else raise "shit happened"
end
As #mudasobwa suggested, [responses].flatten is perhaps the cleanest solution:
[{a: 1}].flatten #=> [{:a=>1}]
[[{a: 1}, {b: 2}]].flatten #=> [{:a=>1}, {:b=>2}]
Clearly this inconsistent response format is not a great API design; but that's unfortunately the nature of working with external resources...
If each hash in the above array does represent a unique object, then I would leave that code as-is. However, if you actually have a different hash for each attribute of a single entity (?!), then you could further clean up the API response as follows:
[{a: 1}].flatten.reduce(:merge) #=> {:a=>1}
[[{a: 1}, {b: 2}]].flatten.reduce(:merge) #=> {:a=>1, :b=>2}
I want to enclose it in an array if it is not already an array.
What about:
result = [result] unless result.is_a? Array
Alternatively, using try_convert:
result = Array.try_convert(result) || [result]

Ruby : Choosing between each, map, inject, each_with_index and each_with_object

When I started writing Ruby many years ago, it took me a while to understand the difference between each and map. It only got worse when I discovered all the other Enumerable and Array methods.
With the help of the official documentation and many StackOverflow questions, I slowly began to understand what those methods did.
Here is what took me even longer to understand though :
Why should I use one method or another?
Are there any guidelines?
I hope this question isn't a duplicate : I'm more interested in the "Why?" than the "What?" or "How?", and I think it could help Ruby newcomers.
A more tl;dr answer:
How to choose between each, map, inject, each_with_index and each_with_object?
Use #each when you want "generic" iteration and don't care about the result. Example - you have numbers, you want to print the absolute value of each individual number:
numbers.each { |number| puts number.abs }
Use #map when you want a new list, where each element is somehow formed by transforming the original elements. Example - you have numbers, you want to get their squares:
numbers.map { |number| number ** 2 }
Use #inject when you want to somehow reduce the entire list to one single value. Example - you have numbers, you want to get their sum:
numbers.inject(&:+)
Use #each_with_index in the same situation as #each, except you also want the index with each element:
numbers.each_with_index { |number, index| puts "Number #{number} is on #{index} position" }
Uses for #each_with_object are more limited. The most common case is if you need something similar to #inject, but want a new collection (as opposed to singular value), which is not a direct mapping of the original. Example - number histogram (frequencies):
numbers.each_with_object({}) { |number, histogram| histogram[number] = histogram[number].to_i.next }
Which object can I use?
First, the object you're working with should be an Array, a Hash, a Set, a Range or any other object that respond to each. If it doesn't, it might be converted to something that will. You cannot call each directly on a String for example, because you need to specify if you'd like to iterate over each byte, character or line.
"Hello World".respond_to?(:each)
#=> false
"Hello World".each_char.respond_to?(:each)
#=> true
I want to calculate something with each element, just like with a for loop in C or Java.
If you want to iterate over each element, do something with it and not modify the original object, you can use each. Please keep reading though, in order to know if you really should.
array = [1,2,3]
#NOTE: i is a bound variable, it could be replaced by anything else (x, n, element). It's a good idea to use a descriptive name if you can
array.each do |i|
puts "La"*i
end
#=> La
# LaLa
# LaLaLa
It is the most generic iteration method, and you could write any of the other mentioned methods with it. We will actually, for pedagogical purposes only. If you spot a similar pattern in your code, you could probably replace it with the corresponding method.
It is basically never wrong to use each, it is almost never the best choice though. It is verbose and not Ruby-ish.
Note that each returns the original object, but this is rarely (never?) used. The logic happens inside the block, and should not modify the original object.
The only time I use each is:
when no other method would do. The more I learn about Ruby, the less often it happens.
when I write a script for someone who doesn't know Ruby, has some programming experience (e.g. C, Fortran, VBA) and would like to understand my code.
I want to get an Array out of my String/Hash/Set/File/Range/ActiveRecord::Relation
Just call object.to_a.
(1..10).to_a
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
"Hello world".each_char.to_a
#=> ["H", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"]
{:a => 1, :b => 2}.to_a
#=> [[:a, 1], [:b, 2]]
Movie.all.to_a #NOTE: Probably very inefficient. Try to keep an ActiveRecord::Relation as Relation for as long as possible.
#=> [Citizen Kane, Trois couleurs: Rouge, The Grapes of Wrath, ....
Some methods described below (e.g. compact, uniq) are only defined for Arrays.
I want to get a modified Array based on the original object.
If you want to get an Array based on the original object, you can use map. The returned object will have the same size as the original one.
array = [1,2,3]
new_array = array.map do |i|
i**2
end
new_array
#=> [1, 4, 9]
#NOTE: map is often used in conjunction with other methods. Here is the corresponding one-liner, without creating a new variable :
array.map{|i| i**2}
#=> [1, 4, 9]
# EACH-equivalent (For pedagogical purposes only):
new_array = []
array.each do |i|
new_array << i**2
end
new_array
#=> [1, 4, 9]
The returned Array will not replace the original object.
This method is very widely used. It should be the first one you learn after each.
collect is a synonym of map. Make sure to use only one of both in your projects.
I want to get a modified Hash based on the original Hash.
If your original object is a Hash, map will return an Array anyway. If you want a Hash back :
hash = {a: 1, b: 2}
hash.map{|key, value| [key, value*2]}.to_h
#=> {:a=>2, :b=>4}
# EACH-equivalent
hash = {a: 1, b: 2}
new_hash = {}
hash.each do |key,value|
new_hash[key]=value*2
end
new_hash
#=> {:a=>2, :b=>4}
I want to filter some elements.
I want to remove nil elements
You can call compact. It will return a new Array without the nil elements.
array = [1,2,nil,4,5]
#NOTE: array.map{|i| i*2} Would raise a NoMethodError
array.compact
# => [1, 2, 4, 5]
# EACH-equivalent
new_array = []
array.each do |integer_or_nil|
new_array << integer_or_nil unless integer_or_nil.nil?
end
new_array
I want to write some logic to determine if an element should be kept in the new Array
You can use select or reject.
integers = (1..10)
integers.select{|i| i.even?}
# => [2, 4, 6, 8, 10]
integers.reject{|i| i.odd?}
# => [2, 4, 6, 8, 10]
# EACH-equivalent
new_array = []
integers.each do |i|
new_array << i if i.even?
end
new_array
I want to remove duplicate elements from your Array
You can use uniq :
letters = %w(a b a b c)
letters.uniq
#=> ["a", "b", "c"]
# EACH-equivalent
uniq_letters = []
letters.each do |letter|
uniq_letters << letter unless uniq_letters.include?(letter)
end
uniq_letters
#TODO: Add find/detect/any?/all?/count
#TODO: Add group_by/sort/sort_by
I want to iterate over all the elements while counting from 0 to n-1
You can use each_with_index :
letters = %w(a b c)
letters.each_with_index do |letter, i|
puts "Letter ##{i} : #{letter}"
end
#=> Letter #0 : a
# Letter #1 : b
# Letter #2 : c
#NOTE: There's a nice Ruby syntax if you want to use each_with_index with a Hash
hash = {:a=>1, :b=>2}
hash.each_with_index{|(key,value),i| puts "#{i} : #{key}->#{value}"}
# => 0 : a->1
# 1 : b->2
# EACH-equivalent
i = 0
letters.each do |letter|
puts "Letter ##{i} : #{letter}"
i+=1
end
each_with_index returns the original object.
I want to iterate over all the elements while setting a variable during each iteration and using it in the next iteration.
You can use inject :
gauss = (1..100)
gauss.inject{|sum, i| sum+i}
#=> 5050
#NOTE: You can specify a starting value with gauss.inject(0){|sum, i| sum+i}
# EACH-equivalent
sum = 0
gauss.each do |i|
sum = sum + i
end
puts sum
It returns the variable as defined by the last iteration.
reduce is a synonym. As with map/collect, choose one keyword and keep it.
I want to iterate over all the elements while keeping a variable available to each iteration.
You can use each_with_object :
letter_ids = (1..26)
letter_ids.each_with_object({}){|i,alphabet| alphabet[("a".ord+i-1).chr]=i}
#=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5, "f"=>6, "g"=>7, "h"=>8, "i"=>9, "j"=>10, "k"=>11, "l"=>12, "m"=>13, "n"=>14, "o"=>15, "p"=>16, "q"=>17, "r"=>18, "s"=>19, "t"=>20, "u"=>21, "v"=>22, "w"=>23, "x"=>24, "y"=>25, "z"=>26}
# EACH-equivalent
alphabet = {}
letter_ids.each do |i|
letter = ("a".ord+i-1).chr
alphabet[letter]=i
end
alphabet
It returns the variable as modified by the last iteration. Note that the order of the two block variables is reversed compared to inject.
If your variable is a Hash, you should probably prefer this method to inject, because h["a"]=1 returns 1, and it would require one more line in your inject block to return a Hash.
I want something that hasn't been mentioned yet.
Then it's probably okay to use each ;)
Notes :
It's a work in progress, and I would gladly appreciate any feedback. If it's interesting enough and fit in one page, I might extract a flowchart out of it.

Modify hashes in an array based on another array

I have two arrays like this:
a = [{'one'=>1, 'two'=>2},{'uno'=>1, 'dos'=>2}]
b = ['english', 'spanish']
I need to add a key-value pair to each hash in a to get this:
a = [{'one'=>1, 'two'=>2, 'language'=>'english'},{'uno'=>1, 'dos'=>2, 'language'=>'spanish'}]
I attempted this:
(0..a.length).each {|c| a[c]['language']=b[c]}
and it does not work. With this:
a[1]['language']=b[1]
(0..a.length).each {|c| puts c}
an error is shown:
NoMethodError (undefined method '[]=' for nil:NilClass)
How can I fix this?
a.zip(b){|h, v| h["language"] = v}
a # => [
# {"one"=>1, "two"=>2, "language"=>"english"},
# {"uno"=>1, "dos"=>2, "language"=>"spanish"}
# ]
When the each iterator over your Range reaches the last element (i.e. a.length), you will attempt to access a nonexisting element of a.
In your example, a.length is 2, so on the last iteration of your each, you will attempt to access a[2], which doesn't exist. (a only contains 2 elements wich indices 0 and 1.) a[2] evaluates to nil, so you will now attempt to call nil['language']=b[2], which is syntactic sugar for nil.[]=('language', b[2]), and since nil doesn't have a []= method, you get a NoMethodError.
The immediate fix is to not iterate off the end of a, by using an exclusive Range:
(0...a.length).each {|c| a[c]['language'] = b[c] }
By the way, the code you posted:
(0..a.length).each {|c| puts c }
should clearly have shown you that you iterate till 2 instead of 1.
That's only the immediate fix, however. The real fix is to simply never iterate over a datastructure manually. That's what iterators are for.
Something like this, where Ruby will keep track of the index for you:
a.each_with_index do |hsh, i| hsh['language'] = b[i] end
Or, without fiddling with indices at all:
a.zip(b.zip(['language'].cycle).map(&:reverse).map(&Array.method(:[])).map(&:to_h)).map {|x, y| x.merge!(y) }
[Note: this last one doesn't mutate the original Arrays and Hashes unlike the other ones.]
The problem you're having is that your (0..a.length) is inclusive. a.length = 2 so you want to modify it to be 0...a.length which is exclusive.
On a side note, you could use Array#each_with_index like this so you don't have to worry about the length and so on.
a.each_with_index do |hash, index|
hash['language'] = b[index]
end
Here is another method you could use
b.each_with_index.with_object(a) do |(lang,i),obj|
obj[i]["language"] = lang
obj
end
#=>[
{"one"=>1, "two"=>2, "language"=>"english"},
{"uno"=>1, "dos"=>2, "language"=>"spanish"}
]
What this does is creates an Enumerator for b with [element,index] then it calls with_object using a as the object. It then iterates over the Enumerator passing in each language and its index along with the a object. It then uses the index from b to find the proper index in a and adds a language key to the hash that is equal to the language.
Please know this is a destructive method where the objects in a will mutate during the process. You could make it non destructive using with_object(a.map(&:dup)) this will dup the hashes in a and the originals will remain untouched.
All that being said I think YAML would be better suited for a task like this but I am not sure what your constraints are. As an example:
yml = <<YML
-
one: 1
two: 2
language: "english"
-
uno: 1
dos: 2
language: "spanish"
YML
require 'yaml'
YAML.load(yml)
#=>[
{"one"=>1, "two"=>2, "language"=>"english"},
{"uno"=>1, "dos"=>2, "language"=>"spanish"}
]
Although using YAML I would change the structure for numbers to be more like language => Array of numbers by index e.g. {"english" => ["zero","one","two"]}. That way you can can access them like ["english"][0] #=> "zero"

Resources