How to display an array with ruby nested each? - ruby

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?

Related

Ruby: How to make a method for additive inverse of an array

I am trying to create a method that when you pass an array to it, a new array is created that is its additive inverse (EX: [1,2,3,4] => (additive inverse) [-1,-2,-3,-4]. I've tried including Enumerable as well as putting .each. I have also gotten rid of .map and then I'm left with undefined method 'x'. I have played around with the code multiple times as well as researched several sites on how to operate on each element within an array as it's passed to a method. The closest I have been is this:
def add_inv (x)
arr_new = x.map {|e| -e}
puts arr_new
end
add_inv([5,-7,8,3])
Output:
-5
7
-8
-3
I'm looking for the output to be in array form.
Desired output: [-5, 7, -8, -3]
Thank you in advance for positive feedback & criticism.
(new to Ruby)
EDIT: 12:29 - 4/13/2020
While playing further with the code and some suggestions below I was able to manipulate an array with the following code and will continue to work on coming up with a method that will allow you to pass an array as an argument and then turn it into the inverse array. Here is the code that gave me the desired output (still not a method though).
b = Array.new ([5,-7,8,3].map{|v| v*(-1)})
p b
Output: [-5,7,-8,-3]
Will update once I figure out how to do this with a method.
sorry, misunderstood your question the first time, the problem here is that puts treat different arrays when you try to show them, you can then use p or print to do what you want, here are the examples
def add_inv_with_puts(x)
puts x.map {|e| -e}
end
add_inv_with_puts([5,-7,8,3])
def add_inv_with_p(x)
p x.map {|e| -e}
end
add_inv_with_p([5,-7,8,3])
def add_inv_with_print(x)
print x.map {|e| -e}
end
add_inv_with_print([5,-7,8,3])
After fooling around with the code some more based on what other posted, I was able to get a method working for any arrays.
def add_inv (x)
#b = Array.new (not needed)
b = x.map{|v| -v}
p b
end
add_inv([1,2,3,4])
add_inv([5,-7,8,3])
Output:
[-1, -2, -3, -4]
[-5, 7, -8, -3]
Thank you for the information and pushes in the right direction.

Sequel gem identity / natural element

I am trying to use a reduce function to &-connect multiple sequel expressions. However in the beginning I'd have the need for a natural element / identity element Sequel-expression. i.e. an expression that can be &-connected without changing the meaning of the query. I tried just using nil, but that does not seem to work. Is there such a thing?^^
Imagine the following
def reduce(buffer, array, &func)
return buffer if array.empty?
if buffer.nil? # I basically want to get rid of this
reduce(array[0], array[1..-1], &func)
else
reduce(func.call(buffer, array[0]), array[1..-1], &func)
end
end
# to be able to call:
reduce(NATURAL_ELEMENT, array_of_expressions) { |first, second| first & second }
When calling Sequel.expr(nil) I get a #<Sequel::SQL::Wrapper #value=>nil> but I don't know exactly what effect that has. Is this what I am looking for?
What you want is a universal set, i.e. a set containing all possible elements. This is the identity for set intersections. If you use Sequel.expr(nil), you'll have the equivalent of an empty set.
I'm not aware of any universal set (or other identity object) in Sequel, but you could try to implement one. Here's an example (using the Singleton module for good measure):
require "singleton"
class UniversalSet
include Singleton
def &(other)
other
end
end
UNIVERSAL_SET = UniversalSet.instance
Now you can use this in set intersection operations. The type of the elements is irrelevant, so it works fine for SQL objects, as well.
UNIVERSAL_SET & [1, 2, 3]
#=> [1, 2, 3]
If you want to, you can also implement the other set operations for the universal set.
This, however, has the downside that it is not commutative. If you try the inverse:
[1, 2, 3] & UNIVERSAL_SET
it will throw an error. This is a limitation in the available coercion constructs in Ruby. (Currently you can only overload operators commutatively for numbers.)
Here's your something for which something & Sequel.expr(field: 1) == Sequel.expr(field: 1):
class IdentityFunction
def self.&(b)
b
end
end
IdentityFunction & "test" #=> "test"
IdentityFunction & 3 #=> 3
You can write :
[[1,2,3,4,5], [2,3,4], [3,4,5]].reduce(IdentityFunction){|a,b| a & b} #=> [3, 4]
Please note that reduce takes the first element as start value by default, so IdentityFunction isn't even needed :
[[1,2,3,4,5], [2,3,4], [3,4,5]].reduce{|a,b| a & b} #=> [3, 4]
Which means what you want to achieve could probably be written as :
array_of_expressions.reduce{ |first, second| first & second }
or just
array_of_expressions.reduce(&:&)

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

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