Extracting multiple entries of a Ruby array - ruby

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

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]

How to display an array with ruby nested each?

Given a list of array, I want to display all pair combination of the array in the form of array of two. For example, let's say I have an array of [1,2,3].
[1, 1]
[1, 2]
[1, 3]
[2, 1]
[2, 2]
[2, 3]
[3, 1]
[3, 2]
[3, 3]
Expected result
This is the function I came up with:
def all_combination(list)
list.each do |i|
list.each do |j|
puts [i,j]
end
end
end
arr1 = [1,2,3] #arr1 can be an array of any size. It will always be integer.
all_combination(arr1)
However, when I do the above code, it displays
1
1
1
2
1
3
...
It seems like there is a gap in my fundamental ruby knowledge. Why puts [i,j] does not work? How can I make it display an actual array like it shows on Expected result section?
I can make it work with puts "#{i}, #{j}", but it does not return an array pair [a,b]
There's a builtin method for what you want:
[1,2,3].permutation(2).each {|a| p a}
If you prefer to stick with your own method (to get "pairs" of items with themselves), change puts [i,j] to either p [i,j] or to puts [i,j].to_s— puts works with strings.
The reason you get the data on each new line is because puts writes each object given to it on a new line. Here is the doc on this:
Writes the given objects to ios as with IO#print. Writes a record
separator (typically a newline) after any that do not already end with
a newline sequence. If called with an array argument, writes each
element on a new line. If called without arguments, outputs a single
record separator.
If Array#permutation does what you want than that is the way to go, however, I don't think it matches what you asked for (ie [1,1],[2,2],[3,3] will not be included).
The following should provide you with what you were originally asking for.
def all_combination(list)
a = []
list.each do |i|
list.each do |j|
a << [i,j]
end
end
a
end
x = all_combination [1,2,3]
p x
puts x
=> [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
1
1
1
2
1
3
2
1
2
2
2
3
3
1
3
2
3
3
x is the returned value you were looking for as shown by p x
ps: this could probably be done with a line or too less using Array#reduce, but this is fairly strait forward.
It seems like there is a gap in my fundamental ruby knowledge. Why puts [i,j] does not work?
Why don't we just look at the documentation?
The documentation for Kernel#puts simply refers us to the documentation for IO#puts:
puts(obj, …) → nil
Equivalent to
$stdout.puts(obj, …)
The documentation for IO#puts says this (bold emphasis mine):
puts(obj, …) → nil
Writes the given objects to ios as with IO#print. Writes a record separator (typically a newline) after any that do not already end with a newline sequence. If called with an array argument, writes each element on a new line. If called without arguments, outputs a single record separator.
As you can see, the documentation clearly states that when you pass an Array to puts, it will print each element on a separate line.
How can I make it display an actual array like it shows on Expected result section?
You cannot display an "actual array". An array is a data structure that maps indices to objects. It's an idea. You cannot display an idea. You can only display text (i.e. a String). So, you need to construct a String that looks like your desired output.
I can make it work with puts "#{i}, #{j}", but it does not return an array pair [a,b]
Well, it doesn't print the brackets because you didn't tell it to print brackets. It will simply print your String, but there aren't any brackets in your String, ergo there won't be brackets in the output. You just have to add them:
puts "[#{i}, #{j}]"
Note: you may be tempted to use Array#inspect, Array#to_s, or Kernel#p, but please don't: their output format is not guaranteed. You may get what you expect, but then again, you may not.
#inspect is for human-readable debugging output. Kernel#p simply calls #inspect. In the particular case of Array#to_s, it is simply an alias for Array#inspect, and the documentation only says:
inspect → string
to_s → string
Creates a string representation of self.
[ "a", "b", "c" ].to_s #=> "[\"a\", \"b\", \"c\"]"
Note that it only says "a string representation", but does not guarantee what string representation it returns.
The way I would approach your problem is something like this:
def all_combination(list)
list.product(list).map {|a, b| "[#{a}, #{b}]" }
end
arr1 = [1,2,3]
puts all_combination(arr1)
I mainly did two things:
Use higher-level collection operations already provided by the Ruby core library (Array#product and Enumerable#map). This reduces the cognitive overhead of understanding the solution. It's now just one line that clearly tells you what it does: first generate the cartesian product of the list with itself, then transform each pair to a suitable string representation.
Separate construction of the output from printing the output. This makes it easier to test: you can simply assert that returns the correct value, instead of having it print directly to the screen, and then jumping through hoops to capture the screen output and compare it to some pre-recorded screen output or something like that.
There are still things to improve, though:
the method does two things: generating the cartesian product and transform it to a string representation
the method naming: this actually hints at #1 above: how can you find a suitable name that describes precisely what a method does, when the method does multiple things at once?

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

How can I perform an idiomatic non-recursive flatten in ruby?

I have a method that returns an array of arrays.
For convenience I use collect on a collection to gather them together.
arr = collection.collect {|item| item.get_array_of_arrays}
Now I would like to have a single array that contains all the arrays.
Of course I can loop over the array and use the + operator to do that.
newarr = []
arr.each {|item| newarr += item}
But this is kind of ugly, is there a better way?
There is a method for flattening an array in Ruby: Array#flatten:
newarr = arr.flatten(1)
From your description it actually looks like you don't care about arr anymore, so there is no need to keep the old value of arr around, we can just modify it:
arr.flatten!(1)
(There is a rule in Ruby that says that if you have two methods that do basically the same thing, but one does it in a somewhat surprising way, you name that method the same as the other method but with an exlamation point at the end. In this case, both methods flatten an array, but the version with the exclamation point does it by destroying the original array.)
However, while in this particular case there actually is a method which does exactly what you want, there is a more general principle at work in your code: you have a sequence of things and you iterate over it and try to "reduce" it down into a single thing. In this case, it is hard to see, because you start out with an array and you end up with an array. But by changing just a couple of small details in your code, it all of the sudden becomes blindingly obvious:
sum = 0
arr.each {|item| sum += item } # assume arr is an array of numbers
This is exactly the same pattern.
What you are trying to do is known as a catamorphism in category theory, a fold in mathematics, a reduce in functional programming, inject:into: in Smalltalk and is implemented by Enumerable#inject and its alias Enumerable#reduce (or in this case actually Array#inject and Array#reduce) in Ruby.
It is very easy to spot: whenever you initialize an accumulator variable outside of a loop and then assign to it or modify the object it references during every iteration of the loop, then you have a case for reduce.
In this particular case, your accumulator is newarr and the operation is adding an array to it.
So, your loop could be more idiomatically rewritten like this:
newarr = arr.reduce(:+)
An experienced Rubyist would of course see this right away. However, even a newbie would eventually get there, by following some simple refactoring steps, probably similar to this:
First, you realize that it actually is a fold:
newarr = arr.reduce([]) {|acc, el| acc += el }
Next, you realize that assigning to acc is completely unnecessary, because reduce overwrites the contents of acc anyway with the result value of each iteration:
newarr = arr.reduce([]) {|acc, el| acc + el }
Thirdly, there is no need to inject an empty array as the starting value for the first iteration, since all the elements of arr are already arrays anyway:
newarr = arr.reduce {|acc, el| acc + el }
This can, of course, be further simplified by using Symbol#to_proc:
newarr = arr.reduce(&:+)
And actually, we don't need Symbol#to_proc here, because reduce and inject already accept a symbol parameter for the operation:
newarr = arr.reduce(:+)
This really is a general pattern. If you remember the sum example above, it would look like this:
sum = arr.reduce(:+)
There is no change in the code, except for the variable name.
arr.inject([]) { |main, item| main += item }
I don't seem to understand the question fully... Is Array#flatten what you are looking for?
[[:a,:b], [1,2,3], 'foobar'].flatten
# => [:a, :b, 1, 2, 3, 'foobar']

Resources