create two arrays based on what meet a condition and the diff - arrays

Lets suppose I've the follow array:
a = [1,2,3]
I want to split it in two arrays from it, one with items for which a condition is true, and other for which the same condition is false:
b, c = a.split_in_two_arrays_or_something_like_that {|x| x == 3}
#=> b = [3]
#=> c = [1,2]
How can I do that in ruby? I don't want to repeat code with something like:
b = a.reject {|x| x == 3}
c = a.reject {|x| x != 3}
Nor iterate over the array twice.
Is there some method that return me something different than the modified array? For example, delete_if will work if it would return the deleted elements, but the original array would've keeped the same, but it doesn't work that way.

Use Enumerable#partition to separate the elements in your array according to a condition. We define the condition in partition's block:
a = [1,2,3]
b, c = a.partition { |x| x == 3 } #=> [[3], [1, 2]]
b #=> [3]
c #=> [1, 2]
This method creates an array with two subarrays.
The first subarray contains the values for which partition's block returns true.
The second subarray contains the values for which partition's block returns false.
Finally we apply parallel-assignment to assign variables b to the first subarray and c to the second.

Related

Is there a way to compare two arrays and return a new array of the common elements which have the same index?

For example
a = [2,3,1,1]
b = [2,7,4,2]
--> c = [2]
My solution was:
c = b.select do
|em| b.index(em) == a.index(em)
end
But if I apply it to the given example it returns
c = [2,2]
Since you want to compare arrays element-wise, zip would be an excellent choice here.
a.zip(b) # => [[2, 2], [3, 7], [1, 4], [1, 2]]
a.zip(b).select {|a1, b1| a1 == b1}.map(&:first) # [2]
# or in ruby 2.7+
a.zip(b).filter_map {|a1, b1| a1 == b1 && a1} # [2]
a = [2,3,1,1]
b = [2,7,4,2]
c = b.select do |em|
b.index(em) == a.index(em)
end
b.select do |em| takes the first element of b (which is 2). b.index(em) finds the first index which contains a 2, which is the first one. (index 0). Since array a has a 2 on the same index, the whole block is true and the element 2 is selected. But the same story is true for the last element. Again, the b array is searched and the first 2 is found at index 0. Hence, that 2 is also selected.
a.each_with_index.to_a & b.each_with_index.to_a
woud give you the common element(s) and their indices.

How to find the key of a hash whose value has the most elements

I'm using Ruby 2.4.
I have a hash whose key is a number and whose value is an array of elements. How do I find the key in the hash with the value that has the most elements? I know that if my value were a single number I could do this:
my_hash.max_by { |k, v| v }
But since the value is an array, I'm not sure how to tell the above to use the number of elements in the array as what should be maxed.
max_by is the correct method :
my_hash = { a: [1, 2], b: [1, 2, 3], c: [5] }
key, longest_array = my_hash.max_by{ |k, array| array.size }
p key
#=> :b
p longest_array
#=> [1, 2, 3]
You just need to specify on which object the comparison should be. In this case, the size of the array value.
You might need to add some checks first : this will only work if all the hash values respond to :size.
You can do it like this:
my_hash.map {|k, v| [k, v.count]}.max_by {|k, v| v}.first
The first map will return an array of two elements arrays. For each of them, the first element is the key and the second is the number of elements of the corresponding array. Then it uses max_by to return the two element array with the maximum number of elements. Finally, the first method returns the key.
I'm not sure if I understood your question correctly, but I'm assuming you have something like this:
my_hash = {1=>[2, 1, 3, 4], 2=>[1, 2], 3=>[1, 4, 6]}
If that's the case, you can get the key for the largest array like this:
my_hash.max_by{|k,v| v.count}.first

How do I ensure an array operation is done by value rather than reference in ruby?

I'm new to ruby and was wondering how I'd go about this
For example:
a = [1,2,3,4]
b = []
b.push(a)
a.pop
a.pop
print b
# => [[1,2]]
I was expecting b to remain [[1,2,3,4]]
a seems to be pushed into b by reference, rather than value. I'd like b to stay as it is regardless of what I do to a in the future; how do I go about doing this in Ruby?
a is an array reference, so to push its value into b, you'll need to copy it:
b.push(a.dup)
This is similar to using strdup in C, where strings are pointers.
You could use splat operator and push elements of a into b instead of whole array a.
b.push(*a)
#=> [1, 2, 3, 4]
If you wanted to push an array, then, use
b.push([*a])
#=> [[1, 2, 3, 4]]

Creating an array from variables without nil values in Ruby

What's the most idiomatic way to create an array from several variables without nil values?
Given these variables:
a = 1
b = nil
c = 3
I would like to create an array ary:
ary #=> [1, 3]
I could use Array#compact:
ary = [a, b, c].compact
ary #=> [1, 3]
But putting everything in an array just to remove certain elements afterwards doesn't feel right.
Using if statements on the other hand produces more code:
ary = []
ary << a if a
ary << b if b
ary << c if c
ary #=> [1, 3]
Is there another or a preferred way or are there any advantages or drawbacks using either of the above?
PS: false doesn't necessarily have to be considered. The variables are either truthy (numbers / strings / arrays / hashes) or nil.
If you are concerned about performance, best way would be probably to use destructive #compact! to avoid allocating memory for second array.
I was hoping for a way to somehow "skip" the nil values during array creation. But after thinking about this for a while, I realized that this can't be achieved because of Ruby's way to handle multiple values. There's no concept of a "list" of values, multiple values are always represented as an array.
If you assign multiple values, Ruby creates an array:
ary = 1, nil, 3
#=> [1, nil, 3]
Same for a method taking a variable number of arguments:
def foo(*args)
args
end
foo(1, nil, 3)
#=> [1, nil, 3]
So even if I would patch Array with a class method new_without_nil, I would end up with:
def Array.new_without_nil(*values)
values.compact!
values
end
This just moves the code elsewhere.
Everything is an object
From an OO point of view, there's nothing special about nil - it's an object like any other. Therefore, removing nil's is not different from removing 1's.
Using a bunch of if statements on the other hand is something I'm trying to avoid when writing object oriented code. I prefer sending messages to objects.
Regarding "advantages or drawbacks":
[...] with compact / compact!
creates full array and shrinks it as needed
short code, often fits in one line
is easily recognized
evaluates each item once
faster (compiled C code)
[...] with << and if statements
creates empty array and grows it as needed
long code, one line per item
purpose might not be as obvious
items can easily be commented / uncommented
evaluates each item twice
slower (interpreted Ruby code)
Verdict:
I'll use compact, might have been obvious.
Here is a solution that uses a hash.
With these values put in an array:
a = 1; b = nil; c = 3; d = nil; e = 10;
ary = [a, b, c, d, e]
There are two nil items in the result which would require a compact to remove both "nil" items.
However the same variables added to a hash:
a = 1; b = nil; c = 3; d = nil; e = 10;
hash = {a => nil, b => nil, c => nil, d => nil, e => nil}
There is just one "nil" item in the result which can easily be removed by hash.delete(nil).

How to find duplicates in array without using `uniq` method

I am doing a challenge to make a method that finds duplicate values in an array, and prints out a new array without the duplicates. Ruby has a built in uniq method; however, I am not allowed to use it.
In my mind, this should work:
def uniques(array)
tempPos = 0
arrayPos = 0
duplicate = true
result = [] # array the result will be "pushed" too
for arrayPos in 0..array.length
for tempPos in 0..array.length
# If the values at the indexes are the same. But the indexes are not the same.
# we have a duplicate
if array[arrayPos] == array[tempPos] && arrayPos != tempPos
duplicate = true
else
duplicate = false
end
if duplicate == false
result[arrayPos] = array[arrayPos]
end
end
puts duplicate
end
puts result.inspect
end
Output:
uniq *this is the short hand user input to run the method*
false
false
false
false
false
false
[1, 2, 1, 4, 5, nil]
I must be doing something wrong.
Are you allowed to use a Set?
require 'set'
array = [1, 2, 3, 3, 3, 4]
Set.new(array).to_a
#=> [1, 2, 3, 4]
An other way is to iterate over every pair in the array:
array.each_cons(2).with_object([array.first]) do |pair, result|
result << pair.last unless pair.first == pair.last
end
#=> [1, 2, 3, 4]
There are many ways to do that. Here's another. Suppose:
arr = [3,5,1,3,4,1,1]
Construct:
h = arr.group_by(&:itself)
#=> {3=>[3, 3], 5=>[5], 1=>[1, 1, 1], 4=>[4]}
The duplicates are given by:
h.select { |_,v| v.size > 1 }.keys
#=> [3, 1]
and an array without the duplicates is given by:
h.keys
#=> [3, 5, 1, 4]
Your logic works fine altough as mentioned above a set would work better. You could also sort the elements, and then find adjacent pairs that are the same value which wouldn't work as well as a set, but would have slightly better run-time than your current solution:
To polish what you currently have:
def uniques(array)
result = [] # array the result will be "pushed" too
for arrayPos in 0...array.length
duplicate = false
for tempPos in 0...result.length
# if the values at the indexes are the same... but the indexes are not the same...
# we have a duplicate
duplicate ||= (array[arrayPos] == result[tempPos])
end
if !duplicate
result << array[arrayPos]
end
end
puts result
end
an slightly better approach (altought still poor performance):
def uniques(array)
result = [] # array the result will be "pushed" too
for arrayPos in 0...array.length
duplicate = result.include?(array[arrayPos])
if !duplicate
result << array[arrayPos]
end
end
puts result
end
Although this solution is OK for a learning assignment, you should note that the complexity of this is O(n^2) (n-squared). What that means is that for an array of size n (for example n=10), you are doing n-squared (100) iterations.
It gets exponentially worse. If you have an array of length 1,000,000, you are doing 1,000,000,000,000 iterations. This is why using a set is so important, it's average run-time will be much lower.
A fairly simple way to so this is to leverage array.include?
new = []
arr.each { |x| new << x unless new.include?(x)}
puts new
That will give you an array (new) that only includes unique elements from the original array (arr)
Duplicate array easy way
arr1 = [1,3,4,5,6,6,6,1]
arry = Array.new(arr1)
puts arry
Find uniq array easy way using OR operator
arr1 = [1,3,4,5,6,6,6,1]
arr2 = Array.new # creating new array
arry = arr1 | arr2 # compare two array using OR operator
puts arry

Resources