Oneliner association from array in Ruby - ruby

In Coffeescript I can do this:
[one..., two] = [1, 2, 3, 4, 5]
# one = [1, 2, 3, 4]
# two = 5
Is there any way to do this (oneliner) in Ruby?
EDIT
I know that I can do this:
one = [1, 2, 3, 4, 5]
two = one.slice!(-1)
EDIT 2
Oneliner could look like this:
two = (one = [1, 2, 3, 4, 5]).slice!(-1)
but this seems too ugly for me.

This should do it:
*one, two = [1, 2, 3, 4, 5]
one
# => [1, 2, 3, 4]
two
# => 5
You can see some more explanations on splat and array destructuring here

irb(main):001:0> a = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
irb(main):002:0> *one, two = a
=> [1, 2, 3, 4, 5]
irb(main):003:0> one
=> [1, 2, 3, 4]
irb(main):004:0> two
=> 5

Related

Duplicate elements of array in ruby

I find a lot of reference about removing duplicates in ruby but I cannot find how to create duplicate.
If I have an array like [1,2,3] how can I map it to an array with dubbed items? [1,1,2,2,3,3]
Is there a method?
Try this one
[1, 2, 3].flat_map { |i| [i, i] }
=> [1, 1, 2, 2, 3, 3]
Here's yet another way, creating the array directly with Array#new :
array = [1, 2, 3]
repetitions = 2
p Array.new(array.size * repetitions) { |i| array[i / repetitions] }
# [1, 1, 2, 2, 3, 3]
According to fruity, #ursus's answer, #ilya's first two answers and mine have comparable performance. transpose.flatten is slower than any of the others.
#Ursus answer is the most clean, there are possible solutions:
a = [1, 2, 3]
a.zip(a).flatten
#=> [1, 1, 2, 2, 3, 3]
Or
a.inject([]) {|a, e| a << e << e} # a.inject([]) {|a, e| n.times {a << e}; a}
=> [1, 1, 2, 2, 3, 3]
Or
[a, a].transpose.flatten # ([a] * n).transpose.flatten
=> [1, 1, 2, 2, 3, 3]
Try this:
[1, 2, 3] * 2
=> [1, 2, 3, 1, 2, 3]
You might want it sorted:
([1, 2, 3] * 2).sort
=> [1, 1, 2, 2, 3, 3]

Ruby code to merge two arrays not working

nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
def mergeArrays (ar1, ar2)
result = (ar1 << ar2).flatten!
require 'pp'
pp %w(result)
end
As simple as this. I am trying to merge these two arrays and display the result. I am also brand-brand new to Ruby. This is the first function I am writing in this language. Trying to learn here. Also how can I remove the duplicates?
It would help if you give example inputs and outputs so we know exactly what you want. When you use the word "merge", I think you actually just want to add the arrays together:
ar1 = [1, 2, 3]
ar2 = [3, 4, 5]
ar3 = ar1 + ar2 # => [1, 2, 3, 3, 4, 5]
Now if you want to remove duplicates, use Array#uniq:
ar4 = ar3.uniq # => [1, 2, 3, 4, 5]
There is no need to write a method to do any of this since the Ruby Array class already supports it. You should skim through the documentation of the Array class to learn more things you can do with arrays.
What do you mean 'not working'?
Similar questions have been asked here:
Array Merge (Union)
You have two options: the pipe operator (a1 | a2) or concatenate-and-uniq ((a1 + a2).uniq).
Also be careful about using <<, this will modify the original variable, concatenating ar2 onto the end of the original ar1.
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
result = (nums1<< nums2).flatten!
nums1
=> [1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
nums2
=> [5, 6, 7, 8, 9]
result
=> [1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
Additionally- just another Ruby tip, you do not need the destructive flatten! with ! versus the regular flatten. The regular flatten method will return a new Array, which you assign to result in your case. flatten! will flatten self in place, altering whatever Array it's called upon, rather than returning a new array.
You can merge Arrays using '+' operator and you can ignore the duplicated values using .uniq
>> nums1 = Array[1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> nums2 = Array[5, 6, 7, 8, 9]
=> [5, 6, 7, 8, 9]
>> def mergeArrays (nums1, nums2)
>> result = (nums1 + nums2).uniq
>> end
=> :mergeArrays
>> mergeArrays(nums1,nums2)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
nums1 = Array[1, 2, 3, 4, 5]
nums2 = Array[5, 6, 7, 8, 9]
p nums1.concat(nums2).uniq

comparing 2 arrays in every position

So what Im trying to accomplish is write a (shorter) condition that makes sure each element is different from the other array. This is confusing but I hope this example clears it up.
array = [1, 2, 3]
new_array = array.shuffle
until array[0] != new_array[0] &&
array[1] != new_array[1] &&
array[2] != new_array[2]
new_array = array.shuffle
end
So what Im doing is making sure that every single element/index pair does not match in the other array.
# [1, 2, 3] => [3, 1, 2] yayyyy
# [1, 2, 3] => [3, 2, 1] not what I want because the 2 didnt move
Is there a better way to do what I want to do? Ive looked up the .any? and .none? but I cant seem to figure out how to implement them. Thanks!
I would do this:
array.zip(new_array).all? { |left, right| left != right }
Here are two approaches that do not involve repeated sampling until a valid sample is obtained:
Sample from the population of valid permutations
Construct the population from which you are sampling:
array = [1, 2, 3, 4]
population = array.permutation(array.size).reject do |a|
a.zip(array).any? { |e,f| e==f }
end
#=> [[2, 1, 4, 3], [2, 3, 4, 1], [2, 4, 1, 3], [3, 1, 4, 2], [3, 4, 1, 2],
# [3, 4, 2, 1], [4, 1, 2, 3], [4, 3, 1, 2], [4, 3, 2, 1]]
Then just choose one at random:
10.times { p population.sample }
# [4, 3, 1, 2]
# [3, 4, 1, 2]
# [3, 4, 1, 2]
# [4, 3, 1, 2]
# [2, 1, 4, 3]
# [2, 1, 4, 3]
# [4, 1, 2, 3]
# [2, 1, 4, 3]
# [4, 3, 1, 2]
# [3, 4, 1, 2]
Sequentially sample for each position in the array
def sample_no_match(array)
a = array.each_index.to_a.shuffle
last_ndx = a[-1]
a.dup.map do |i|
if a.size == 2 && a[-1] == last_ndx
select = a[-1]
else
select = (a-[i]).sample
end
a.delete(select)
array[select]
end
end
10.times.each { p sample_no_match(array) }
# [2, 4, 3, 1]
# [4, 3, 1, 2]
# [2, 1, 3, 4]
# [1, 3, 4, 2]
# [1, 3, 2, 4]
# [1, 3, 2, 4]
# [1, 4, 3, 2]
# [3, 4, 2, 1]
# [1, 3, 4, 2]
# [1, 3, 4, 2]
I have been unable to prove or disprove that the second method produces a random sample. We can, however, determine relative frequencies of outcomes:
n = 500_000
h = n.times.with_object(Hash.new(0)) { |_,h| h[sample_no_match(array)] += 1 }
h.keys.each { |k| h[k] = (h[k]/(n.to_f)).round(4) }
h #=> {[1, 2, 3, 4]=>0.0418, [2, 1, 3, 4]=>0.0414, [1, 4, 2, 3]=>0.0418,
# [3, 4, 2, 1]=>0.0417, [4, 3, 2, 1]=>0.0415, [3, 1, 4, 2]=>0.0419,
# [2, 3, 1, 4]=>0.0420, [4, 2, 3, 1]=>0.0417, [3, 2, 1, 4]=>0.0413,
# [4, 2, 1, 3]=>0.0417, [2, 1, 4, 3]=>0.0419, [1, 3, 2, 4]=>0.0415,
# [1, 2, 4, 3]=>0.0418, [1, 3, 4, 2]=>0.0417, [2, 4, 1, 3]=>0.0414,
# [3, 4, 1, 2]=>0.0412, [1, 4, 3, 2]=>0.0423, [4, 1, 3, 2]=>0.0411,
# [3, 2, 4, 1]=>0.0411, [2, 4, 3, 1]=>0.0418, [3, 1, 2, 4]=>0.0419,
# [4, 3, 1, 2]=>0.0412, [4, 1, 2, 3]=>0.0421, [2, 3, 4, 1]=>0.0421}
avg = (h.values.reduce(:+)/h.size.to_f).round(4)
#=> 0.0417
mn, mx = h.values.minmax
#=> [0.0411, 0.0423]
([avg-mn,mx-avg].max/avg).round(6)
#=> 0.014388
which means that the maximum deviation from the average was only 1.4% percent of the average.
This suggests that the second method is a reasonable way of producing pseudo-random samples.
Initially, the first line of this method was:
a = array.each_index.to_a
By looking at the frequency distribution for outcomes, however, it was clear that that method did not produce a pseudo-random sample; hence, the need to shuffle a.
Here's one possibility:
until array.zip(new_array).reject{ |x, y| x == y }.size == array.size
new_array = array.shuffle
end
Note, though, that it will break for arrays like [1] or [1, 1, 1, 2, 3], where the number of instances of 1 exceeds half the size of the array. Recommend Array#uniq or similar, along with checking for arrays of sizes 0 or 1, depending on how trustworthy your input is!

how to get remain of `delete_at` without destruction

I want to delete a value from an Array and get remain of it like this:
a = [1, 2, 3, 4]
=> [1, 2, 3, 4]
a.delete_at(2)
=> 3
a
=> [1, 2, 4]
How can I get [1, 2, 4] without destruction of the variable?
There are a couple of ways you can do, admittedly, they don't seem very elegant:
a[0..1] + a[3..-1]
# => [1, 2, 4]
a.dup.tap { |x| x.delete_at(2) }
# => [1, 2, 4]
a.values_at(0..1, 3..-1)
# => [1, 2, 4]
Personally, I think the way which conveys your intention best is:
a.reject.with_index { |_, i| i == 2 }
# => [1, 2, 4]

How to make an array containing a duplicate of an array

I couldn't find a way to build an array such as
[ [1,2,3] , [1,2,3] , [1,2,3] , [1,2,3] , [1,2,3] ]
given [1,2,3] and the number 5. I guess there are some kind of operators on arrays, such as product of mult, but none in the doc does it. Please tell me. I missed something very simple.
Array.new(5, [1, 2, 3]) or Array.new(5) { [1, 2, 3] }
Array.new(size, default_object) creates an array with an initial size, filled with the default object you specify. Keep in mind that if you mutate any of the nested arrays, you'll mutate all of them, since each element is a reference to the same object.
array = Array.new(5, [1, 2, 3])
array.first << 4
array # => [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
Array.new(size) { default_object } lets you create an array with separate objects.
array = Array.new(5) { [1, 2, 3] }
array.first << 4
array #=> [[1, 2, 3, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
Look up at the very top of the page you linked to, under the section entitled "Creating Arrays" for some more ways to create arrays.
Why not just use:
[[1, 2, 3]] * 5
# => [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
The documentation for Array.* says:
...returns a new array built by concatenating the int copies of self.
Typically we'd want to create a repeating single-level array:
[1, 2, 3] * 2
# => [1, 2, 3, 1, 2, 3]
but there's nothing to say we can't use a sub-array like I did above.
It looks like mutating one of the subarrays mutates all of them, but that may be what someone wants.
It's like Array.new(5, [1,2,3]):
foo = [[1, 2, 3]] * 2
foo[0][0] = 4
foo # => [[4, 2, 3], [4, 2, 3]]
foo = Array.new(2, [1,2,3])
foo[0][0] = 4
foo # => [[4, 2, 3], [4, 2, 3]]
A work-around, if that's not the behavior wanted is:
foo = ([[1, 2, 3]] * 2).map { |a| [*a] }
foo[0][0] = 4
foo # => [[4, 2, 3], [1, 2, 3]]
But, at that point it's not as convenient, so I'd use the default Array.new(n) {…} behavior.

Resources