Rajesh Omanakuttan
Rajesh Omanakuttan

Reputation: 6928

Compare two dimensional arrays

I have two two-dimensional arrays,

a = [[17360, "Z51.89"],
 [17361, "S93.601A"],
 [17362, "H66.91"],
 [17363, "H25.12"],
 [17364, "Z01.01"],
 [17365, "Z00.121"],
 [17366, "Z00.129"],
 [17367, "K57.90"],
 [17368, "I63.9"]]

and

b = [[17360, "I87.2"],
 [17361, "s93.601"],
 [17362, "h66.91"],
 [17363, "h25.12"],
 [17364, "Z51.89"],
 [17365, "z00.121"],
 [17366, "z00.129"],
 [17367, "k55.9"],
 [17368, "I63.9"]]

I would like to count similar rows in both the arrays irrespective of the character case, i.e., "h25.12" would be equal to "H25.12".

I tried,

count = a.count - (a - b).count

But (a - b) returns

[[17360, "Z51.89"],
 [17361, "S93.601A"],
 [17362, "H66.91"],
 [17363, "H25.12"],
 [17364, "Z01.01"],
 [17365, "Z00.121"],
 [17366, "Z00.129"],
 [17367, "K57.90"]]

I need the count as 5 since there are five similar rows when we do not consider the character case.

Upvotes: 0

Views: 516

Answers (8)

Cary Swoveland
Cary Swoveland

Reputation: 110755

I have assumed that the ith element of a is to be compared with the ith element of b. (Edit: a subsequent comment by the OP confirmed this interpretation.)

I would be inclined to use indices to avoid the construction of relatively large temporary arrays. Here are two ways that might be done.

#1 Use indices

[a.size,b.size].min.size.times.count do |i|
  af,al=a[i]
  bf,bl=b[i]; 
  af==bf && al.downcase==bl.downcase
end
  #=> 5

#2 Use Refinements

My purpose in giving this solution is to illustrate the use of Refinements. I would not argue for its use for the problem at hand, but this problem provides a good vehicle for showing how the technique can be applied.

I could not figure out how best to do this, so I posted this question on SO. I've applied @ZackAnderson's answer below.

module M
  refine String do
    alias :dbl_eql :==
    def ==(other)
      downcase.dbl_eql(other.downcase)
    end
  end

  refine Array do
    def ==(other)
      zip(other).all? {|x, y| x == y}
    end
  end
end

'a' == 'A'         #=> false (as expected)
[1,'a'] == [1,'A'] #=> false (as expected)

using M
'a' == 'A'         #=> true
[1,'a'] == [1,'A'] #=> true

I could use Enumerable#zip, but for variety I'll use Object#to_enum and Kernel#loop in conjunction with Enumerator#next:

ea, eb = a.to_enum, b.to_enum
cnt = 0
loop do
   cnt += 1 if ea.next == eb.next
end
cnt #=> 5    

Upvotes: 0

Mark Thomas
Mark Thomas

Reputation: 37527

You can zip them and then use the block form of count:

a.zip(b).count{|e| e[0][1].downcase == e[1][1].downcase}

Upvotes: 1

user3118220
user3118220

Reputation: 1468

It will convert second element of inner array to upcase for both array then you can perform subtraction, then It will return exact result that you want

a.map{|first,second| [first,second.upcase]} - b.map{|first,second| [first,second.upcase]}

Upvotes: 1

Sharvy Ahmed
Sharvy Ahmed

Reputation: 7405

Using Proc and '&':

procedure = Proc.new { |i, j| [i, j.upcase] }
(a.map(&procedure) & b.map(&procedure)).count
#=> 5

For better understanding, let's simplify it:

new_a = a.map {|i, j| [i, j.upcase]}
new_b = b.map {|i, j| [i, j.upcase]}

# Set intersection using '&'
(new_a & new_b).count
#=> 5

Upvotes: 0

Jechol Lee
Jechol Lee

Reputation: 145

You want to count similar, so &(AND) operation is more suitable.

(a.map { |k, v| [k, v.upcase] } & b.map { |k, v| [k, v.upcase] }).count

Upvotes: 0

Santhosh
Santhosh

Reputation: 29174

You can convert Arrays to Hash, and use Enumerable#count with a block.

b_hash = b.to_h
a.to_h.count {|k, v| b_hash[k] && b_hash[k].downcase == v.downcase }
# => 5 

Upvotes: 2

shivam
shivam

Reputation: 16506

Instead of a - b you should do this:

a.map{|k,v| [k,v.downcase]} - b.map{|k,v| [k,v.downcase]} # case-insensitive

Upvotes: 3

SteveTurczyn
SteveTurczyn

Reputation: 36880

a.count - (a.map{|e| [e[0],e[1].downcase] } - b.map{|e| [e[0],e[1].downcase] }).count

The above maps a and b to new arrays where the second sub-array element is downcase.

Upvotes: 0

Related Questions