What does a comma followed by an equals sign mean in Ruby? - ruby

Just saw something like this in some Ruby code:
def getis;gets.split.map(&:to_i);end
k,=getis # What is this line doing?
di=Array::new(k){Array::new(k)}

It assigns the array's first element using Ruby's multiple assignment:
a, = [1, 2, 3]
a #=> 1
Or:
a, b = [1, 2, 3]
a #=> 1
b #=> 2
You can use * to fetch the remaining elements:
a, *b = [1, 2, 3]
a #=> 1
b #=> [2, 3]
Or:
*a, b = [1, 2, 3]
a #=> [1, 2]
b #=> 3

It works like this. If lhs has single element and rhs has multiple values then lhs gets assigned an array of values, like this.
a = 1,2,3 #=> a = [1,2,3]
Whereas if lhs has more elements than rhs, then excess elements in lhs are discarded
a,b,c = 1,2 #=> a = 1, b = 2, c = nil
Therefore
a, = 1,2,3 #=> a = 1. The rest i.e. [2,3] are discarded

Related

A single method to delete! elements from Array by predicate and return deleted

I couldn't find a method in docs to do the following:
a = [1, 2, 3]
b = a.remove! {|x| x > 1}
puts a # [1]
puts b # [2, 3]
There is a select! method that does similar thing but it doesn't accept a predicate.
To my disappointment, delete_if, keep_if, reject! and select! mutate the array but also return the same array.
Currently, I achieve a desired in 2 steps like this but maybe there are better/smarter options?
a = [1, 2, 3]
b = a.reject {|x| x > 1}
a = a - b
puts a # [1]
puts b # [2, 3]
I don't know a way to accomplish that in 1 step, however if as in your 2nd example you'd accept to not mutate a from the ruby method, you could use Enumerable#partition
a = [1, 2, 3]
# note that b is first because first array is for elements returning true
b, a = a.partition { |x| x > 1 }
puts a # [1]
puts b # [2, 3]

How to multiply each number in array by 2 in ruby

I have an array with characters, numbers and float values.
a[] = {'a',2,2.5}
I have to multiply each integer and float by 2 and no operation should be done on character.
My solution -
def self.double_numbers(input)
input.map do |input_element|
if input_element.is_a? Integer
input_element.to_i * 2
elsif input_element.is_a? Float
input_element.to_Float * 2
end
end
end
It is not working, for input
a[] = {'a',2,2.5}
It is returning
0 4 4
You could use map and over each element in the array check if is Numeric, and if so, then multiply it by 2, then you can compact your result for nil values:
p ['a', 2, 2.5].map{|e| e * 2 if e.is_a? Numeric}.compact
# => [4, 5.0]
If you want to leave the elements which won't be applied the *2 operation, then:
p ['a', 2, 2.5].map{|e| e.is_a?(Numeric) ? e * 2 : e}
Also you could use grep to simplify the checking, and then map your only-numeric values:
p ['a', 2, 2.5].grep(Numeric).map{|e| e*2}
# => [4, 5.0]
I don't know the side effects of doing this, but looks good (of course if the output won't be only Numeric objects):
class Numeric
def duplicate
self * 2
end
end
p ['a', 2, 2.5].grep(Numeric).map(&:duplicate)
Or also:
p ['a', 2, 2.5].grep(Numeric).map(&2.method(:*))
# [4, 5.0]
I have to multiply each integer and float by 2 and no operation should
be done on character.
here you go:
> a = ["a", 2, 2.5]
> a.map{|e| e.is_a?(Numeric) ? e * 2 : e}
#=> ["a", 4, 5.0]
Note: a[] = {'a',2,2.5} not a correct syntax for array.

How to remember swapped array elements in another array?

This is driving me crazy! I've been trying to write a Ruby method to find all permutations, to solve Project Euler's problem 24. When I swap the elements of an array, they are swapped properly. But when I try to STORE this swapped array in a DIFFERENT array, this new array only remembers the latest copy of my swapped array! It won't remember the older version.
When I print out a during the loop, it shows all permutations properly. But when I print out perm (which I use to store all different permutations of a), it only shows 1 version of a repeated several times. How do I fix this?
a = [0, 1, 2, 3]
perms = []
p "a = #{a}" # output: "a = [0, 1, 2, 3]"
perms << a # add a to perms array
p "perms = #{perms}" # output: "perms = [[0, 1, 2, 3]]"
a[0], a[1] = a[1], a[0] # swap 1st 2 elements of a
p "a = #{a}" # output: "a = [1, 0, 2, 3]"
perms << a # add a to perms array
p "perms = #{perms}" # "perms = [[1, 0, 2, 3], [1, 0, 2, 3]]"
a[1], a[2] = a[2], a[1] # swap 2nd 2 elements of a
p "a = #{a}" # "a = [1, 2, 0, 3]"
perms << a # add a to perms array
p "perms = #{perms}" # "perms = [[1, 2, 0, 3], [1, 2, 0, 3], [1, 2, 0, 3]]"
Thanks to Sawa below, both "dup" and "clone" methods solved my problem! Why doesn't my original way work? When would I use "dup" vs. "clone"? Please give me some code examples.
a[0], a[1] = a[1], a[0] # swap 1st 2 elements of a
p "a = #{a}" # output: "a = [1, 0, 2, 3]"
b = a.dup (or a.clone)
perms << b
p "perms = #{perms}" # "perms = [[0, 1, 2, 3], [1, 0, 2, 3]]" *** it remembers!
a[1], a[2] = a[2], a[1] # swap 2nd 2 elements of a
p "a = #{a}" # "a = [1, 2, 0, 3]"
b = a.dup (or a.clone)
perms << b
p "perms = #{perms}" # "perms = [[0, 1, 2, 3], [1, 0, 2, 3], [1, 2, 0, 3]]"
Variables in Ruby (with some exceptions, such as variables bound to integers) contain references to objects, not values. Here's an example from running "irb":
1.9.3p374 :021 > str1="hi"
=> "hi"
1.9.3p374 :022 > str2=str1
=> "hi"
1.9.3p374 :023 > str1.replace("world")
=> "world"
1.9.3p374 :024 > str2
=> "world"
You'll notice that once I replace the value for str1, str2's "value" changes as well. That's because it contains a reference to the str1 object. I know one difference between dup and clone has to do with the "freeze" method. If I had called str1.freeze, then it would prevent the object str1 references from being modified, e.g.,
1.9.3p374 :055 > str1.freeze
=> "hi"
1.9.3p374 :056 > str1[0]="b"
RuntimeError: can't modify frozen String
from (irb):56:in `[]='
from (irb):56
from /.rvm/rubies/ruby-1.9.3-p374/bin/irb:13:in `<main>
"Dup"-ing a frozen object doesn't create a frozen object whereas cloning does.
EDIT: just a slight update....When assigning an object on the right to a variable on the left (e.g., str = Object.new), the variable receives an object reference. When assigning one variable to another, the left-hand side variable receives a copy of the reference that the variable on the right contains. In either case, you are still storing object references in the left-hand side variable.
Your original didn't work because you kept modifying the same array instance a.
Take a dup of the original array each time before you modify it into a different array. Or, create a new instance of Array by not relying on a destructive method.
a = original_array
b = a.dup
... # do some modifications to `b`
perms << b
c = a.dup
... # do some modifications to `c`
perms << c
...
If you don't like reinventing the wheel, you can use the facets gem.
gem install facets
https://github.com/rubyworks/facets/blob/d96ec0d700d1d7180ccbb5452e0a926386ec0b32/lib/backport/facets/array/permutation.rb
require 'facets'
[1, 2, 3].permutation
#=> [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

Confusion with splat operator and Range in Ruby

I was trying to see how splat operator worked with range in Ruby. To do so ran the below code in my IRB:
*a = (1..8)
#=> 1..8
When the above is fine, what happened with below? means why a gives []?
*a,b = (1..8)
#=> 1..8
b
#=> 1..8
a
#=> []
means why b gives []?
a,*b = (1..8)
#=> 1..8
a
#=> 1..8
b
#=> []
What precedence took place in the below Rvalues ?
a,*b = *(2..8),*3,*5
# => [2, 3, 4, 5, 6, 7, 8, 3, 5]
b
# => [3, 4, 5, 6, 7, 8, 3, 5]
a
# => 2
Here is another try to the splat operator(*) :-
While I know that in parallel assignment we couldn't use multiple splatted variable, but why not the same when splat is used with Rvalues?
*a,*b = [1,2,3,4,5]
SyntaxError: (irb):1: syntax error, unexpected tSTAR
*a,*b = [1,2,3,4,5]
^
from /usr/bin/irb:12:in `<main>'
The above is as expected.
a = *2,*3,*5
#=> [2, 3, 5]
But couldn't understand the above.
I think of parallel assignment as setting an array of variables equal to another array with pattern matching.
One point is that a range is a single value until you convert it to an array or splat it. For instance [1..5] which is a one element array of the range 1..5 and not [1,2,3,4,5]. To get the array of ints you need to do (1..5).to_a or [*(1..5)]
The first one i think is the trickiest. If the splatted var is assigned to one element, the var itself must be a one-element array:
*a = 5
a
# => [ 5 ]
For the next two, splat takes 0 or more not already assigned values into an array. So the following makes sense:
*a, b = (1..8)
is like
*a, b = "hey"
which is like
*a, b = [ "hey" ]
so *a is [] and b is "hey" and by the same logic that if *a is nothing, a must be an empty array. Same idea for
a, *b = (1..5)
For the next one, the range is splatted, so the assignment makes a lot of sense again:
[*(2..4), 9, 5]
# => [2, 3, 4, 9, 5]
And parallel assignment with a splat again. Next one is similar:
[*3, *4, *5]
# => [3, 4, 5]
So that's like
a = 3, 4, 5
which is like
a = [3, 4, 5]
splat has a very low precedence, almost anything will be executed earlier than the splat.
The code is splatting but the result is thrown away: b = *a = (1..8); p b #=> [1, 2, 3, 4, 5, 6, 7, 8]

Ruby Logical Operators - Elements in one but not both arrays

Let's say I have two arrays:
a = [1,2,3]
b = [1,2]
I want a logical operation to perform on both of these arrays that returns the elements that are not in both arrays (i.e. 3). Thanks!
Arrays in Ruby very conveniently overload some math and bitwise operators.
Elements that are in a, but not in b
a - b # [3]
Elements that are both in a and b
a & b # [1, 2]
Elements that are in a or b
a | b # [1, 2, 3]
Sum of arrays (concatenation)
a + b # [1, 2, 3, 1, 2]
You get the idea.
p (a | b) - (a & b) #=> [3]
Or use sets
require 'set'
a.to_set ^ b
There is a third way of looking at this solution, which directly answers the question and does not require the use of sets:
r = (a-b) | (b-a)
(a-b) will give you what is in array a but not b:
a-b
=> [3]
(b-a) will give you what is in array b but not a:
b-a
=> []
OR-ing the two array subtractions will give you final result of anything that is not in both arrays:
r = ab | ba
=> [3]
Another example might make this even more clear:
a = [1,2,3]
=> [1, 2, 3]
b = [2,3,4]
=> [2, 3, 4]
a-b
=> [1]
b-a
=> [4]
r = (a-b) | (b-a)
=> [1, 4]

Resources