Three ways to create a range, hash, array in ruby - ruby

I am doing a tutorial course on ruby and it asks for 3 ways to create range, hash, array.
I can only think of 2: (1..3) and Range.new(1,3) (and similarly for hash and array).
What is the third way?
The tutorial in question is The Odin Project

Ranges may be constructed using the s..e and s...e literals, or with ::new.
Ranges constructed using .. run from the beginning to the end inclusively.
Those created using ... exclude the end value. When used as an iterator, ranges return each value in the sequence.
(0..2) == (0..2) #=> true
(0..2) == Range.new(0,2) #=> true
(0..2) == (0...2) #=> false
Read More Here

For Arrays there's Array::[] (example taken directly from the docs):
Array.[]( 1, 'a', /^A/ ) # => [1, "a", /^A/]
Array[ 1, 'a', /^A/ ] # => [1, "a", /^A/]
[ 1, 'a', /^A/ ] # => [1, "a", /^A/]
Similarly there's Hash::[]. Not sure about Ranges; in fact, the docs (as far as I can tell) only mention literals and Range::new.
I can't see why you'd use these over a literal, but there you go.

You can also make a exclusive range, using (1...4), which if turned into an array would become [1, 2, 3]
(1..3) is an inclusive range, so it contains all numbers, from 1 to 3, but if you used (1...3), having 3 dots instead of 2 makes it exclusive, so it contains all numbers from 1, up to but not including 3.
As for arrays and hashes, #to_a, Array#[], #to_h, and Hash#[] will work.
(1..3).to_a
=> [1, 2, 3]
Array[1, 2, 3]
=> [1, 2, 3]
[[1, 2], [3, 4], [5, 6]].to_h
=> {1=>2, 3=>4, 5=>6}
Hash[ [[1, 2], [3, 4], [5, 6]] ]
=> {1=>2, 3=>4, 5=>6}
But they are probably looking for Array#[] and Hash#[] on the array and hash part.

Related

Why a new call of a method with exclamation mark affects all previous calls of that method?

I'm sorry if this is a duplicate - I couldn't find anything similar in the existing posts.
I understand the difference between methods like shuffle and shuffle!. However, I am confused why calling the method more than once would result in changing the variables of all objects that previously referred to it? I'd expect once we apply a method, that the variable gets a value and we're done with it. Not that it continues to refer to the method call and the argument passed and that it would get re-evaluated later on.
I thought it's best to demonstrate with an example:
irb(main):001:1* def shuffle(arr)
irb(main):002:1* arr.shuffle!
irb(main):003:0> end
=> :shuffle
irb(main):004:0> arr = [1,2,3,4]
=> [1, 2, 3, 4]
irb(main):005:0> one = shuffle(arr)
=> [4, 2, 3, 1]
irb(main):006:0> two = shuffle(arr)
=> [1, 2, 4, 3]
irb(main):007:0> one
=> [1, 2, 4, 3]
So, here I'd expect one to stay [4, 2, 3, 1]. However, with each new call, all previous ones would get equated to the latest result of the method call. I realise it should have something to do with calling it with the same argument arr, but still doesn't quite make sense.
Array#shuffle! shuffles the array in-place and returns its receiver:
ary = [1, 2, 3, 4]
ary.equal?(ary.shuffle!) #=> true
Assigning the result from shuffle! to another variable doesn't change this. It merely results in two variables referring to the same array:
a = [1, 2, 3, 4]
b = a.shuffle!
a #=> [2, 4, 1, 3]
b #=> [2, 4, 1, 3]
a.equal?(b) #=> true
You probably want a new array. That's what Array#shuffle (without !) is for:
a = [1, 2, 3, 4]
b = a.shuffle
a #=> [1, 2, 3, 4]
b #=> [2, 4, 1, 3]
Even if shuffle returns the element in the original order, you'll get another array instance:
a = [1, 2, 3, 4]
b = a.shuffle until b == a
a #=> [1, 2, 3, 4]
b #=> [1, 2, 3, 4]
a.equal?(b) #=> false

How to use a recursive array

I have array, named a and define it with [1, 2, 3].
Next, I pushed it to itself:
a = [1, 2, 3]
a << a
and the result I get is:
#=> [1, 2, 3, [...]]
When I want to get the last element of array using a.last I get:
a.last
#=> [1, 2, 3, [...]]
#even
a.last.last.last
#=> [1, 2, 3, [...]]
What is going on, when we would push array to itself?
Yes, I understand that this should create a recursive array, but what can we do with it?
In Ruby variables, array elements etc. are object references. So when you do a = [1, 2, 3], there will be an array somewhere in memory and the a variable is a reference to that memory. Now when you do a << a, a[4] will also be a reference to that object. So in effect a now contains a reference to itself.
a = [1, 2, 3]
a << a.dup
a.last
=> [1, 2, 3]
a.last.last
=> 3
Maybe this is what you wanted. This just insert an array [1, 2, 3] as the last item of the a array. In the way you did you put a reference at the end of the a array and this becomes recursive.

How to insert an array in the middle of an array?

I have a Ruby array [1, 4]. I want to insert another array [2, 3] in the middle so that it becomes [1, 2, 3, 4]. I can achieve that with [1, 4].insert(1, [2, 3]).flatten, but is there a better way to do this?
You could do it the following way.
[1,4].insert(1,*[2,3])
The insert() method handles multiple parameters. Therefore you can convert your array to parameters with the splat operator *.
One form of the method Array#[]= takes two arguments, index and length. When the latter is zero and the rvalue is an array, the method inserts the elements of the rvalue into the receiver before the element at the given index (and returns the rvalue). Therefore, to insert the elements of:
b = [2,3]
into:
a = [1,4]
before the element at index 1 (4), we write:
a[1,0] = b
#=> [2,3]
a #=> [1,2,3,4]
Note:
a=[1,4]
a[0,0] = [2,3]
a #=> [2,3,1,4]
a=[1,4]
a[2,0] = [2,3]
a #=> [1,4,2,3]
a=[1,4]
a[4,0] = [2,3]
a #=> [1,4,nil,nil,2,3]]
which is why the insertion location is before the given index.
def insert_array receiver, pos, other
receiver.insert pos, *other
end
insert_array [1, 4], 1, [2, 3]
#⇒ [1, 2, 3, 4]
or, the above might be achieved by monkeypatching the Array class:
class Array
def insert_array pos, other
insert pos, *other
end
end
I believe, this is short enough notation to have any additional syntax sugar. BTW, flattening the result is not a good idea, since it will corrupt an input arrays, already having arrays inside:
[1, [4,5]].insert 1, *[2,3]
#⇒ [1, 2, 3, [4,5]]
but:
[1, [4,5]].insert(1, [2,3]).flatten
#⇒ [1, 2, 3, 4, 5]
My option without array#insert method
array = [1,2,3,6,7,8]
new_array = [4,5]
array[0...array.size/2] + new_array + array[array.size/2..-1]

How to convert arrays of numbers into a matrix in Ruby

I have a two simple arrays of numbers, representing the cartesian position of an object.
a = [3, 4]
b = [8, 5]
I want to check if "a" and "b" are beside each other. I would like to convert the two into a matrix and perform a subtractions of the two positions, and then check if the absolute value of either element is "1".
Is there a way to do this?
You're getting the uninitialized constant error because you first need:
require 'matrix'
Then you could just:
Matrix[a,b]
Sample interactive output:
irb(main):011:0> require 'matrix'
=> true
irb(main):012:0> Matrix[a,b]
=> Matrix[[3, 4], [8, 5]]
I don't think using Matrix class methods is justified here. The only method that would be marginally useful is Matrix#-, but to use that you need to convert your arrays to Matrix objects, apply Matrix#-, then convert the resultant matrix object back to an array to determine if the absolute value of any element equals one (whew!). I'd just do this:
def adjacent?(a,b)
a.zip(b).any? { |i,j| (i-j).abs == 1 }
end
adjacent?([3, 4], [8, 5]) #=> true
adjacent?([3, 7], [8, 5]) #=> false
adjacent?([3, 7], [2, 5]) #=> true
For the first example:
a = [3, 4]
b = [8, 5]
c = a.zip(b)
#=> [[3, 8], [4, 5]]
c.any? { |i,j| (i-j).abs == 1 }
#=> true
The last statements determines if either of the following is true.
(3-8).abs == 1
(4-5).abs == 1

Ruby split array into X groups

I need to split an array into X smaller array. I don't care about the number of elements in the smaller arrays I just need to create X arrays from a larger one. I've been doing some reading and it seems like I need a method similar to the in_groups method from rails. I am not using rails right now, just ruby.
Requiring Rails just to get that function is overkill. Just use each_slice:
team = ['alice', 'andy', 'bob', 'barry', 'chloe', 'charlie']
=> ["alice", "andy", "bob", "barry", "chloe", "charlie"]
team.each_slice(2).to_a
=> [["alice", "andy"], ["bob", "barry"], ["chloe", "charlie"]]
each_slice's parameter is the number of elements in each slice (except possibly the last slice). Since you're looking for X slices, you can do something like this:
team.each_slice(team.length/X).to_a
That's not perfect, as you'll get one extra slice if the array length is not a multiple of X, but gets you in the ballpark and you can tweak it from there depending on what exactly your needs are.
Since you say you don't care how many are in each, you could just use the length/x approach above, then check to see if you have one too many slices. If so, just join the last two slices into one jumbo-size slice. This might avoid some fussy math or floating point operations.
You can make your own method, here's a basic idea:
class Array
def my_group(x)
start = 0
size = (self.size() / Float(x)).ceil
while x > 0
yield self[start, size]
size = ((self.size() - 1 - start) / Float(x)).ceil
start += size
x -= 1
end
end
end
%w(1 2 3 4 5 6 7 8 9 10).my_group(3) {|group| p group}
# =>["1", "2", "3", "4"]
# =>["4", "5", "6"]
# =>["7", "8", "9"]
I decided to put:
require 'active_support/core_ext/array/grouping'
if x is a count of groups:
x = 2
a = [1,2,3,4,5,6,7,8,9,10,11,12]
a.in_groups(x)
=> [[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]]
if group by x pieces:
x = 2
a = [1,2,3,4,5,6,7,8,9,10,11,12]
a.each_slice(x).to_a
=> [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]
If you need to have N groups, you can use the in_groups monkey-patch provided by ActiveSupport, as mentioned in another answer:
require 'active_support/core_ext/array/grouping'
my_array = [1,2,3,4,5]
my_array.in_groups(2)
# => [[1, 2, 3], [4, 5, nil]]
my_array.in_groups(2, false)
# => [[1, 2, 3], [4, 5]]
If you care about the number of elements in the group as opposed to the number of groups, you can use each_slice provided by Ruby core:
my_array = [1,2,3,4,5]
my_array.each_slice(2).to_a
# => [[1, 2], [3, 4], [5]]

Resources