Who can explain this? - ruby

I am having trouble understanding this comment.
Array({:a => "a", :b => "b"}) #=> [[:a, "a"], [:b, "b"]]
Could you explain how it works in detail?

{:a => "a", :b => "b"} creates a Hash.
Passing that to Array will create an array of arrays. Each array element of the outer array will be another array containing the key and the value of one item of the hash.

The Array methods transforms your hash into an array.
Therefore, for each entry of the hash, ruby will create an array with two elements : the key and the value of the entry in the hash.
You have two entries in your array :
:a => "a" which becomes [:a, "a"]
:b => "b" which becomes [:b, "b"]

It's actually a method provided by Kernel module.
Firstly it tries to call to_ary(return self for array), then to_a on argument.
You'll get the same result by using corresponding methods to_ary and to_a.

Related

How to convert an array of strings into values (symbols)?

How can I easily convert
a = [ "b", "c", "d" ]
into
a = [ :b, :c, :d ]
and vice versa?
I went through the docs but didn't find a solution
To iterate over an array, returning a new array with updated values, use Array#map. That's the key method you need to know about.
I'm not sure what you mean by "converting strings into values", but objects like :b in ruby are called Symbols.
You can convert a String into a Symbol by calling to_sym. (And vice-versa by calling to_s.)
So putting this all together, you can convert the array by doing this:
a = [ "b", "c", "d" ]
a.map { |letter| letter.to_sym }
# => [:b, :c, :d]
# Or equivalently, there's a shorthand for this:
a.map(&:to_sym)
If you want to update the original array, rather than return a new array, use Array.map! instead of Array.map:
you can call to_sym on each element in a map:
a.map { |letter| letter.to_sym }
=> [:b, :c, :d]

Convert a string to a path of keys in a hash

I have a string like string = "this_is_a_test" and a hash hash. How can I convert the string to a path of keys, so that it returns the value located at: hash['this']['is']['a']['test']?
I don't want to simply split the string into an array; I want to use the string as keys to access in a hash. The keys already exist in the hash.
Inject works nicely here:
hash = "this_is_a_test".split('_').reverse.inject("final value") { |h, s| {s => h} }
This returns:
{"this"=>{"is"=>{"a"=>{"test"=>"final value"}}}}
and:
hash['this']['is']['a']['test']
=> "final value"
The explanation here is that each iteration of inject returns a hash that contains the current string as a key, and the previous hash as a value, so each new key contains recursively all the hashes up to this point, including the deepest value that is passed as argument of inject.
This is why the array of keys needs to be reversed, because the hash is created from the inside-out.
edit: I believe I didn't understand the question correctly.
You actually meant to access an existing recursive hash.
Assuming the hash has been built using my previous method, accessing the innermost value can be achieved with inject too:
"this_is_a_test".split('_').inject(hash) { |h,v| h[v] }
=> "final value"
Also note that Ruby 2.3 implements the new Hash#dig method, which does exactly this, in a secure way:
path = "this_is_a_test".split('_')
hash.dig(*path) # => "final value"
could be something like this ?
keys = ["a", "b", "c"]
values = [1, 2, 3]
zipped = keys.zip(values)
=> [["a", 1], ["b", 2], ["c", 3]]
Hash[zipped]
=> {"a"=>1, "b"=>2, "c"=>3}
you are looking for String.split() function
var array = "this_is_a_test".split('_');

How to sort! arrays in ruby

I want to sort my_array and then reverse the order.
Which markup is correct?
my_array.sort.reverse!
or
my_array.sort!.reverse
Or does it make any difference?
Thanks
You have to decompose the chain :
First, let's understand the difference between the sort and the sort! method.
If I write
array = [7,2,4]
array.sort!
array # => [2,4,7]
If you write
array = [7,2,4]
foo = array.sort
array # => [7,2,4]
foo # => [2,4,7]
The sort method sort the array and returns the result as the output of the function, whereas the sort! one directly modifies the existing array.
So if you write :
my_array.sort.reverse!
It is like writing :
(my_array.sort). # => Here we create a new array who is calculated by sorting the existing one
reverse! # => Then we reverse this new array, who is not referenced by a variable.
If you write :
(my_array.sort!). #=> Here you sort my_array and reinject the result into my_array !
reverse # Then reverse it and inject the result into a NEW array
So in both cases, you will not obtain what you want ! What you want to do is either :
my_array.sort!.reverse!
or :
new_array = my_array.sort.reverse
You'll get the same output, but one will modify the initial array. See this:
2.1.1 :001 > my_array = ["a","d","b","c"]
=> ["a", "d", "b", "c"]
Just declaring an array with a, b, c, and d in completely wrong orders.
2.1.1 :002 > my_array.sort.reverse!
=> ["d", "c", "b", "a"]
Running your first command on it returns a reverse-sorted array
2.1.1 :003 > my_array
=> ["a", "d", "b", "c"]
... but doesn't modify the original array itself.
2.1.1 :004 > my_array.sort!.reverse
=> ["d", "c", "b", "a"]
Running the second command returns the same result, the array sorted backwards
2.1.1 :005 > my_array
=> ["a", "b", "c", "d"]
But the array itself has been modified, but only by the sort! call. A ! after a method call 'saves' the changes to the object it's called on and returns the result. So in the second one:
You sort the array, saving the changes to it and returning the sorted array, then
Run reverse on the sorted array, which doesn't save to anything and only returns the result.
my_array.sort!.reverse will modify the receiver so my_array will be sorted after this call e.g.
my_array = [1,4,3,5,2]
my_array.sort!.reverse
#=> [5,4,3,2,1]
my_array
#=> [1,2,3,4,5]
the second form my_array.sort.reverse! will not modify my_array because sort will dup the array first then reverse! will modify this duped copy which is not being stored. This would have the same impact as my_array.sort.reverse without the !
my_array = [1,4,3,5,2]
my_array.sort.reverse!
#=> [5,4,3,2,1]
my_array
#=> [1,4,3,5,2]
Thirdly, something like my_array.sort!.reverse! will modify the receiver twice meaning my_array.sort! will sort the array in place and then .reverse! will reverse the array in place. so my_array will now be sorted and reversed.
my_array = [1,4,3,5,2]
my_array.sort!.reverse!
#=> [5,4,3,2,1]
my_array
#=> [5,4,3,2,1]
Although in this case I do not think you will need either bang ! method as the second form has no impact. bang ! in ruby means "dangerous", may alter the data or have other unexpected results.
my_array.sort!.reverse!
is the correct answer, since you want to change the array you already have.
my_array.sort.reverse!
creates a sorted copy and reverses it (the original doesn't change).
my_array.sort!.reverse
sorts the original and creates a reversed copy (the original isn't reversed).

Getting an array of hash values given specific keys

Given certain keys, I want to get an array of values from a hash (in the order I gave the keys). I had done this:
class Hash
def values_for_keys(*keys_requested)
result = []
keys_requested.each do |key|
result << self[key]
end
return result
end
end
I modified the Hash class because I do plan to use it almost everywhere in my code.
But I don't really like the idea of modifying a core class. Is there a builtin solution instead? (couldn't find any, so I had to write this).
You should be able to use values_at:
values_at(key, ...) → array
Return an array containing the values associated with the given keys. Also see Hash.select.
h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
h.values_at("cow", "cat") #=> ["bovine", "feline"]
The documentation doesn't specifically say anything about the order of the returned array but:
The example implies that the array will match the key order.
The standard implementation does things in the right order.
There's no other sensible way for the method to behave.
For example:
>> h = { :a => 'a', :b => 'b', :c => 'c' }
=> {:a=>"a", :b=>"b", :c=>"c"}
>> h.values_at(:c, :a)
=> ["c", "a"]
i will suggest you do this:
your_hash.select{|key,value| given_keys.include?(key)}.values

How to remove elements of array in place returning the removed elements

I have an array arr. I want to destructively remove elements from arr based on a condition, returning the removed elements.
arr = [1,2,3]
arr.some_method{|a| a > 1} #=> [2, 3]
arr #=> [1]
My first try was reject!:
arr = [1,2,3]
arr.reject!{|a| a > 1}
but the returning blocks and arr's value are both [1].
I could write a custom function, but I think there is an explicit method for this. What would that be?
Update after the question was answered:
partition method turns out to be useful for implementing this behavior for hash as well. How can I remove elements of a hash, returning the removed elements and the modified hash?
hash = {:x => 1, :y => 2, :z => 3}
comp_hash, hash = hash.partition{|k,v| v > 1}.map{|a| Hash[a]}
comp_hash #=> {:y=>2, :z=>3}
hash #=> {:x=>1}
I'd use partition here. It doesn't modify self inplace, but returns two new arrays. By assigning the second array to arr again, it gets the results you want:
comp_arr, arr = arr.partition { |a| a > 1 }
See the documentation of partition.
All methods with a trailing bang ! modify the receiver and it seems to be a convention that these methods return the resulting object because the non-bang do so.
What you can to do though is something like this:
b = (arr.dup - arr.reject!{|a| a>1 })
b # => [2,3]
arr #=> [1]
Here is a link to a ruby styleguide which has a section on nameing - although its rather short
To remove (in place) elements of array returning the removed elements one could use delete method, as per Array class documentation:
a = [ "a", "b", "b", "b", "c" ]
a.delete("b") #=> "b"
a #=> ["a", "c"]
a.delete("z") #=> nil
a.delete("z") { "not found" } #=> "not found"
It accepts block so custom behavior could be added, as needed

Resources