Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing two items in array with Ruby

Tags:

ruby

I'm having trouble with situations in which I need to compare two elements in an array using methods. I find the logic quite simple just using a nested loop but this probably isn't good use of Ruby.

For ex. Determine if array has any pair of 2 numbers that equal 0:

def pairs(array)
  i = 0
  while i < array.length
    y = i + 1
    while y < array.length
      if array[i] + array[y] == 0
        return true
      end
      y += 1
    end
    i += 1
  end
  return false
end

Or if i wanted to see if two things in an array are identical I would use the same logic except set: if array[i] == to array[y]...

Can someone provide a better method to a problem like this?

like image 458
Steve Avatar asked Nov 24 '15 22:11

Steve


People also ask

How do you compare two arrays in Ruby?

Arrays can be equal if they have the same number of elements and if each element is equal to the corresponding element in the array. To compare arrays in order to find if they are equal or not, we have to use the == operator.

What does .last do in Ruby?

The . last property of an array in Ruby returns the last element of the array.

What is .first in Ruby?

The first() is an inbuilt method in Ruby returns an array of first X elements. If X is not mentioned, it returns the first element only. Syntax: range1.first(X) Parameters: The function accepts X which is the number of elements from the beginning. Return Value: It returns an array of first X elements.


4 Answers

Often, you can translate the English specification quite directly into Ruby.

In your first question, you ask if any combination of two elements adds to zero. The method to use when you want to know whether something is true for any element of an enumerable is Enumerable#any?. If you want to work on combinations of elements from an array, you use the method Array#combination. For summing, you can either use Numeric#+ or Enumerable#inject, and if you want to know whether a number is zero, you can use Numeric#zero?.

So, a possible implementation of your first question would be:

ary.combination(2).any? {|pair| pair.inject(:+).zero? }

Your second question could be answered by this:

ary.combination(2).any? {|a, b| a == b }

In both cases, there are of course other ways to do this. For example, in the first case, we could observe that the only way that two numbers sum to zero is if one is the negative of the other.

Note that none of the things that can typically go wrong in a loop can happen here. No off-by-one errors, no fencepost errors, no wrong terminating condition, no iterating off the end of the array, simply because there is no loop.

like image 190
Jörg W Mittag Avatar answered Oct 23 '22 16:10

Jörg W Mittag


If you just want to count an element's occurencies in an array you can use Enumerable#count

a = [1,2,3,4,5,1,2,2]
a.count(2)
# => 3

now if you want to see if there are duplicate entries in an array you can use the uniq method

def has_duplicates?(arr)
  arr.uniq.length == arr.length
end
like image 39
xlembouras Avatar answered Oct 23 '22 15:10

xlembouras


Here's my approach to your first question and another method that returns the pairs:

def pairs?(arr)
    arr.inject(false){|c, v| c || (arr.include?(-v) && v > 0)}
end

def get_pairs(arr)
    arr.collect{|val| [val, -val] if arr.include?(-val) && val > 0}.reject(&:nil?)
end

arr = [1, 8, -2, 12, -15, 5, 3]
p pairs?(arr)
p get_pairs(arr)

arr = [1, 8, -2, 12, -15, 5, 3, 2, -3, 1]
p pairs?(arr)
p get_pairs(arr)

Output:

false
[]
true
[[3, -3], [2, -2]]

They don't work when there's a pair of zeros, though. You can handle that case explicitly or perhaps someone could offer a better solution.

like image 1
Jason Avatar answered Oct 23 '22 15:10

Jason


You are going to like Ruby, in part because you generally won't be using indices to pull out or set the elements of an array.

Code

def pairs(arr)
  arr.map { |e| e < 0 ? -e : e }.
      group_by(&:itself).
      select { |_,v| v.size > 1 }.
      keys
end 

Examples

pairs [1, 8, -2, 12, -15, 5, 3]           #=> []
pairs [1, 8, -2, 12, -15, 5, 3, 2, -3, 1] #=> [1, 2, 3] 

If you want the second example to return [[1, -1], [2, -2], [3, -3]] (though I don't see the point), replace the penultimate line of the method with:

map { |k,_| [k, -k] }

Explanation

The steps for:

arr = [1, 8, -2, 12, -15, 5, 3, 2, -3, 1]

are:

a = arr.map { |e| e < 0 ? -e : e }
  #=> [1, 8, 2, 12, 15, 5, 3, 2, 3, 1] 
b = a.group_by(&:itself)
  #=> {1=>[1, 1], 8=>[8], 2=>[2, 2], 12=>[12], 15=>[15], 5=>[5], 3=>[3, 3]} 

We were given Object#itself in Ruby v2.2. For earlier versions, use:

b = a.group_by { |e| e }

Continuing:

c = b.select { |_,v| v.size > 1 }
  #=> {1=>[1, 1], 2=>[2, 2], 3=>[3, 3]} 
c.keys
  #=> [1, 2, 3] 

The line:

select { |_,v| v.size > 1 }

could be written:

select { |k,v| v.size > 1 }

where k and v represent "key" and "value", but since k is not used in the block calculation it is common practice to replace k with the local variable _ (yes, it's a variable--try it in IRB), mainly to inform the reader that the argument is not being used in the block.

If arr = [1, 1, -1, -1, 2, -2], this will return [1,2]. If you want it to return [1,1,2], you must take a different approach.

like image 1
Cary Swoveland Avatar answered Oct 23 '22 14:10

Cary Swoveland