using methods on hash elements - ruby

So I semi-asked this in another thread about how to get .max and return a value to a screen. All where very good answers, I just didn't ask the entire question. I ended up going with:
hash_example = {777 =>["dog","brown",3], 123=>["cat","orange",2]} #hash example
h =hash_example.values.collect{|a|a[0]}.max #change .max value based on element
puts the a[1] element based on what is returned in h because of .max of a[0].max
The problem is now I want to take h (the .max value found) and based on finding that element return a different element from the same array in the next line of code. To further elaborate lets say the above code found dog as .max. How do I go about returning brown or 3 to the screen in the next line of code?
puts hash_example.some_method_here{block of useful code using the h value} ?
I'm probably looking into this the wrong way or is it just a simple puts statment ? I've tried some nesting in the block but I'm definetly not nesting it correctly. .inject and .map I think are the right direction but I'm not writing the block correctly.

You're probably best off sorting the hash values, and taking the last one (as the max value), then working from there.
>> h = {777 =>["dog","brown",3], 123=>["cat","orange",2]}
=> {777=>["dog", "brown", 3], 123=>["cat", "orange", 2]}
>> h.values.sort_by{|a|a[0]}.last[1]
=> "brown"
The sort_by method accepts a block that describes what you want to sort by, relative to a single element - in this case it's using the first array element.

Here is a way of finding the max that will also give you the other array elements...
e = {777=>["dog", "brown", 3], 123=>["cat", "orange", 2]}
>> e.values.transpose[0].max
=> "dog"
So we can rewrite the code from the top...
x = e.values
t = x.transpose[0]
x[t.index t.max]
Which returns ["dog", "brown", 3]

Related

Ruby map gives weird results

Consider the following code
a="123456789"
t=[[1,4],[3,4],[4,5],[1,2]]
p t.map{|x,y|
a[x],a[y]=a[y],a[x]
#p a
a
}
I know ruby map method collects the last expression of the given block but when using the above code to swap the chars in a using the indexes in t won't succeeds.My intention was to collect the state of a after each swap in the index of t.But map always gives the array of a which is in the last state ie)["135264789", "135264789", "135264789", "135264789"].
The results shows that the map method have collected the final result of a after completing each indexes in t.But when printing the a after each swap prints correct value of a at each state.
Is this the correct behavior or am i missing something?
This is because the String#[]= method mutates the string.
Quick fix would be something like this:
a="123456789"
t=[[1,4],[3,4],[4,5],[1,2]]
p t.map{|x,y]
b = "#{a}" # IMPORTANT - this builds a new string
b[x],b[y]=b[y],b[x] # this mutates the new string
#p b
b
}
An alternative to "#{a}" would be to say a.clone, it does the same thing in this case.
The reason this works, is because instead of directly modifying a with a[x],a[y]=a[y],a[x], you're making a temporary copy of a and modifying that instead
edit - I misread the question - if you want to show the result of chaining each operation on the previous result, use dup/clone after the modification as Stefan said in his answer
Is my understanding correct?
Yes, I believe it is. I second what Max says, and I'll also elaborate a bit in case it helps.
Each b is a newly created object because it gets created inside the block, so it gets recreated with every new iteration. The a is created outside the block, so the same object (a) keeps getting referenced inside the block for each iteration.
You can better understand how this works by experimenting with #object_id. Try running this code:
a="123456789"
t=[[1,4],[3,4],[4,5],[1,2]]
p t.map { |x,y|
b = "#{a}" # IMPORTANT - this builds a new string
b[x],b[y]=b[y],b[x]
p "a.object_id = #{a.object_id}"
p "b.object_id = #{b.object_id}"
b
}
You will notice that a is the same object for each iteration of the #map method, while b is a new one.
This is an example of the concept of a closure. A closure is some sort of enclosed code structure that retains access to whatever state is available in the context in which it was created, while that context doesn't have access to its, the enclosed code's, state. Sort of like a "one way mirror": the enclosed code can see outside, but the outside can't see into the enclosed code.
In Ruby, closures are implemented as blocks: blocks are closures. So, everything that is visible to whatever context a block is created in (in this case, main) is also visible to that block, although the reverse is not true — for example, you can't reference b from outside the block. (Methods are not closures: if your block were a method, it wouldn't be able to see a unless you passed it in as an argument to your method.)
So, as Max says, when you make changes to a inside your block, you are actually changing (mutating) the same a that you defined up top each time.
Side topic
Now, if you are referencing individual characters in strings it's important to understand that the underlying structure of strings differs from that of arrays. Also, arrays behave differently when you mutate their elements from strings when you mutate their characters.
I'm mentioning this because I have this vague feeling that you are thinking of string character references as pretty much analogous to array element references. This is pretty much only true with respect to syntax.
You may find the results of running this code interesting:
a = '123456789'
p a.object_id
p a[0].object_id
p a[1].object_id
a[0] = '7'
p a.object_id
p a[0].object_id
p a[1].object_id
puts
a = '123456789'.chars
p a.object_id
p a[0].object_id
p a[1].object_id
a[0] = '7'
p a.object_id
p a[0].object_id
p a[1].object_id
In particular, a comparison of the four outputs of a[1].object_id should be instructive, because it shows where strings and arrays differ. If you reassign an element in an array, that element and only that element gets a new object id. If you reassign a character in a string, the string object itself remains the same, but every character in the string gets recreated.
Since you are returning a from map, the result will contain a four times. Those a's all refer to the same object.
You probably want to return a copy of a to preserve its current state:
a = '123456789'
t = [[1, 4], [3, 4], [4, 5], [1, 2]]
r = t.map { |x, y|
a[x], a[y] = a[y], a[x]
a.dup
}
r #=> ["153426789", "153246789", "153264789", "135264789"]

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.

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?

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.

Resources