what is the mean of * and flatten in ruby - ruby

I am new to ruby language so when I was trying to sort a hash by value
I used this method to sort:
movie_popularity.sort_by{|m,p| p}.reverse
but the the sort method returns an array while I need a hash to be returned so I used this command:
movie_popularity=Hash[*movie_popularity.sort_by{|m,p| p}.reverse.flatten]
my Question is what is the meaning of * and flatten in the above line?
Thanks =)

The * is called the "splat operator"; I'm not sure I could give you the technical definition (though I'm sure you'd find it soon enough with Google's help), but the way I'd describe it is that it basically takes the place of hand-writing multiple comma-separated values in code.
To make this more concrete, consider the case of Hash[] which you've used in your example. The Hash class has a [] class method which takes a variable number of arguments and can normally be called like this:
# Returns { "foo" => 1, "bar" => 2 }
h = Hash["foo", 1, "bar", 2]
Notice how that isn't an array or a hash or anything that I passed in; it's a (hand-written) sequence of values. The * operator allows you to achieve basically the same thing using an array--in your case, the one returned by movie_popularity.sort_by{|m,p| p}.reverse.flatten.
As for that flatten call: when you call sort_by on a hash, you're really leveraging the Enumerable module which is included in a variety of classes (most notably Array and Hash) that provide enumeration. In the case of a hash, you've probably noticed that instead of iterating over one like this:
hash.each { |value| ... }
Instead you do this:
hash.each { |key, value| ... }
That is, iterating over a hash yields two values on each iteration. So your sort_by call on its own would return a sequence of pairs. Calling flatten on this result collapses the pairs into a one-dimensional sequence of values, like this:
# Returns [1, 2, 3, 4]
[[1, 2], [3, 4]].flatten

'flatten' flattens an array: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-flatten
'*' is the splat operator: http://theplana.wordpress.com/2007/03/03/ruby-idioms-the-splat-operator/
The pertinent bit in the last url is this:
a = [[:planes, 21], [:cars, 36]]
h = Hash[*a] # => { :planes=>21, :cars=>36}

Related

Ruby shorthand for adding items to array in each loop

I feel like i've seen this question somewhere before but i cannot find it and i'm pretty sure ruby has a one liner for looping over something and adding the items to an array:
items = []
some_stuff.each do |stuff|
items << stuff
end
items
this way?
items = some_stuff.inject([]) { |acc, item| acc + [item] }
obviously this exact snippet doesn't make sense because it's another array with the same value, you could just dup some_stuff, but I think can help you anyway
Is this what you are looking for?
some_stuff=[1,2,3,4,6,7]
Code
items=some_stuff.each_with_object([]){|value,items|items.push(value)}
p items
Output
[1, 2, 3, 4, 6, 7]
If some_stuff responds to each, then it very likely also conforms to the Enumerable protocol. (It would be silly not to, since each is all you need to support the Enumerable protocol via include Enumerable.)
The Enumerable protocol includes the method Enumerable#to_a, which returns the elements of the Enumerable as an Array:
to_a → array
Returns an array containing the items in self:
(0..4).to_a # => [0, 1, 2, 3, 4]
So, assuming that some_stuff really does conform to the Enumerable protocol, all you need to do is
items = some_stuff.to_a
However, unless you absolutely need the specific features an Array provides, there is no need to even do this. If some_stuff really does conform to the Enumerable protocol, then most of the methods that you would use on the Array already exist on some_stuff as well, and they may even be more efficient because some_stuff knows more about what its internal structure is and what the structure of the elements is than Array does.
There is often no need to convert an Enumerable to an Array.
You could use splat operator if working with an array
items = [*some_stuff]

Extracting multiple entries of a Ruby array

Let's say I have an array
arry = ["a","b","c","d","e","f","g"]
and I want to get the fourth, second, and seventh element of that array.
Let's say some other function determined which are the needed values and passed me backan array of indices like [3, 1, 6]. In most languages I could write this
array[3,1,6] #=> ["d","b","g"] (or "dbg"?)
or
array[3 1 6] #=> ["d","b","g"]
But that won't work in Ruby, of course. Is there a simple way to do this in Ruby? The cleanest I can find is something like:
[3,1,6].map { |i| array[i] }
I really tried to find a duplicate to this question since it seems so basic, but I just couldn't.
This is remarkably easy to do in most languages, so I'm almost assuming I'm just overlooking the remarkably obvious.
array.values_at(3,1,6) #=> ["d","b","g"]
If your other function returns an array then you must flatten it before you can pass it as parameters to values_at. Like this
arry = ["a","b","c","d","e","f","g"]
def indices
[1,3,6]
end
puts arry.values_at(*indices)
output
b
d
g

Why does this enumerator.to_a returns []?

I just encounter this piece of code
Enumerator.new((1..100), :take, 5).to_a
# => []
Does anyone know why it returns an empty array and not an array of 5 integers?
From the docs, Enumerator#new:
new(obj, method = :each, *args)
In the second, deprecated, form, a generated Enumerator iterates over
the given object using the given method with the given arguments
passed.
Use of this form is discouraged. Use Kernel#enum_for or Kernel#to_enum
instead.
This second usage (which you should not use according to the documentation), needs a each-like method (that's it, one that yields values). take returns values, but does not yield them, so you get an empty enumerable.
Note that in Ruby 2 it will be plain simple to perform a lazy take:
2.0.0dev> xs = (1..100).lazy.take(5)
#=> #<Enumerator::Lazy: #<Enumerator::Lazy: 1..100>:take(5)>
2.0.0dev> xs.to_a
#=> [1, 2, 3, 4, 5]

Index a ruby array to omit an element or range of elements?

I have a ruby array of, say, 10 elements, and I'd like to return all but the 5th element.
a = *(1..10)
I'd like to be able to do something like
a[0..3 + 5..9]
Or even better something like (borrowing from the R syntax),
a[-4]
to do this, but that doesn't work. (Nor does anything more clever I've tried like getting an array of the element indices). What's the best way to do this in ruby?
You can use values_at: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-values_at
so you may use it as
a.values_at(0..3, 5..9)
No black magic required:
a[0..3] + a[5..9]
Look in the documentation under Array#delete_at
For example, myarray.delete_at(5) results in the array lacking what was element 5 (counting from 0, of course).
Example:
class Array
def without(n)
self.delete_at(n)
return self
end
end
arr = [1, 2, 3, 4]
p arr.without(1) #=> [1, 3, 4]
However, as a commenter points out, this alters the original array, which may not be what the O.P. wants! If that's an issue, write it like this:
class Array
def without(n)
arr2 = Array.new(self)
arr2.delete_at(n)
return arr2
end
end
That returns the desired array (an array lacking the nth element of the original) while leaving the original array untouched.

Can someone explain a real-world, plain-language use for inject in Ruby?

I'm working on learning Ruby, and came across inject. I am on the cusp of understanding it, but when I'm the type of person who needs real world examples to learn something. The most common examples I come across are people using inject to add up the sum of a (1..10) range, which I could care less about. It's an arbitrary example.
What would I use it for in a real program? I'm learning so I can move on to Rails, but I don't have to have a web-centric example. I just need something that has a purpose I can wrap my head around.
Thanks all.
inject can sometimes be better understood by its "other" name, reduce. It's a function that operates on an Enumerable (iterating through it once) and returns a single value.
There are many interesting ways that it can be used, especially when chained with other Enumerable methods, such as map. Often times, it can be a more concise and expressive way of doing something, even if there is another way to do it.
An example like this may seem useless at first:
range.inject {|sum, x| sum += x}
The variable range, however, doesn't have to be a simple explicit range. It could be (for example) a list of values returned from your database. If you ran a database query that returned a list of prices in a shopping cart, you could use .inject to sum them all and get a total.
In the simple case, you can do this in the SQL query itself. In a more difficult case, such as where some items have tax added to them and some don't, something like inject can be more useful:
cart_total = prices.inject {|sum, x| sum += price_with_tax(x)}
This sort of thing is also particularly useful when the objects in the Enumerable are complex classes that require more detailed processing than a simple numerical value would need, or when the Enumerable contains objects of different types that need to be converted into a common type before processing. Since inject takes a block, you can make the functionality here as complex as you need it to be.
Here are a couple of inject() examples in action:
[1, 2, 3, 4].inject(0) {|memo, num| memo += num; memo} # sums all elements in array
The example iterates over every element of the [1, 2, 3, 4] array and adds the elements to the memo variable (memo is commonly used as the block variable name). This example explicitly returns memo after every iteration, but the return can also be implicit.
[1, 2, 3, 4].inject(0) {|memo, num| memo += num} # also works
inject() is conceptually similar to the following explicit code:
result = 0
[1, 2, 3, 4].each {|num| result += num}
result # result is now 10
inject() is also useful to create arrays and hashes. Here is how to use inject() to convert [['dogs', 4], ['cats', 3], ['dogs', 7]] to {'dogs' => 11, 'cats' => 3}.
[['dogs', 4], ['cats', 3], ['dogs', 7]].inject({'dogs' => 0, 'cats' => 0}) do |memo, (animal, num)|
memo[animal] = num
memo
end
Here is a more generalized and elegant solution:
[['dogs', 4], ['cats', 3], ['dogs', 7]].inject(Hash.new(0)) do |memo, (animal, num)|
memo[animal] = num
memo
end
Again, inject() is conceptually similar to the following code:
result = Hash.new(0)
[['dogs', 4], ['cats', 3], ['dogs', 7]].each do |animal, num|
result[animal] = num
end
result # now equals {'dogs' => 11, 'cats' => 3}
Instead of a range, imagine you've got a list of sales prices for some item on eBay and you want to know the average price. You can do that by injecting + and then dividing by the length.
ActiveRecord scopes are a typical case. If we call scoped on a model, we get an object on which we can chain additional scopes. This lets us use inject to build up a search scope from, say, a params hash:
search_params = params.slice("first_name","last_name","city","zip").
reject {|k,v| v.blank?}
search_scope = search_params.inject(User.scoped) do |memo, (k,v)|
case k
when "first_name"
memo.first_name(v)
when "last_name"
memo.last_name(v)
when "city"
memo.city(v)
when "zip"
memo.zip(v)
else
memo
end
(Note: if NO params are supplied, this brings back the whole table, which might not be what you wanted.)
My favorite explanation for inject or it's synonym reduce is:
reduce takes in an array and reduces it to a single value. It does this by iterating through a list, keeping and transforming a running total along the way.
I found it in a wonderful article at
http://railspikes.com/2008/8/11/understanding-map-and-reduce

Resources