views:

241

answers:

6

is there any other simple,nicer way?

require 'pp'

a1 = ["02/28/10","Webinars","131","0","26 Feb 2010","0","3d, 8h, 49m, 18s"]
a2 = ["02/20/10","Webinars","131","9","26 Feb 2010","0","3d, 8h, 49m, 18s"]

def compare(array1,array2,ignore)

  tmp1 = Array.new
  tmp2 = Array.new
  0.upto(array1.length-1) {|index|
    if !ignore.include?(index)
      tmp1 << array1[index]
      tmp2 << array2[index]
    end
  }
  if tmp1 == tmp2 
    return true
  else
    return false    
  end
end

pp a1
pp a2
puts
puts compare(a1,a2,[0,3])

and the output is

["02/28/10", "Webinars", "131", "0", "26 Feb 2010", "0", "3d, 8h, 49m, 18s"]
["02/20/10", "Webinars", "131", "9", "26 Feb 2010", "0", "3d, 8h, 49m, 18s"]

true
+2  A: 

This looks better to me :) :

def compare(array1, array2 = [], ignore = [])
  return false if array1.length != array2.length
  array1.each_with_index.all? do |e1, i1|
    array2[i1] == e1 || ignore.include?(i1)
  end
end

The beauty of this is that it "chains" each_with_index with all? making much cleaner code.
The bad is that it only works from Ruby 1.8.7. Anyway do not see a reason to use < 1.8.7

Dmytrii Nagirniak
I don't like that this mutates the original arrays.
Matchu
It doesn't anymore.
Dmytrii Nagirniak
@Dmitriy Nagirnyak: you code gives me an error `each_with_index': no block given (LocalJumpError) but most importantly I tried that already. delete_at(i) causes that the second element in ignore array will be incorrect as the indexes will change. And also it will modify the original array passed as argument. Of course I can use some tmp array.
Radek
Accidentally revoked downvote; wish I could put it back. This returns a false positive on `compare([1,2],[1,2,3],[])`
Matchu
I've updated the answer not to mutate arrays. It works for me (Ruby 1.8.7). The arrays `[1,2]` and `[1,2,3]` are not supposed to be equal. Current method doesn't check for that (if needed it is easy to do it, but I haven't observed such a requirement). I'll update anyway.
Dmytrii Nagirniak
Right. Run this definition of `compare([1,2],[1,2,3],[])`, which should clearly be false, and it returns `true`.
Matchu
well my arrays are going to have always the same size. Of course your solution is more general ... the way I like it
Radek
In response to comment edit: I prefer to write what the function *should* do, rather than just beat the one test case the OP provides, in case he uses `compare` later and expects it to have what would typically be expected behavior.
Matchu
@Mathcu. Sorry, I was updating my comment. Look at my prev comment.
Dmytrii Nagirniak
A'ight. I'm satisfied xD
Matchu
@Mathcu, YAGNI :)
Dmytrii Nagirniak
I am getting this error `each_with_index': no block given (LocalJumpError)` using `ruby 1.8.6 (2008-08-11 patchlevel 287) [i386-`mswin32]`
Radek
@Dmitriy Nagirnyak: why did you do that? `array1 = []`
Radek
Bad luck :) I updated the answer. Upgrading to 1.8.7 is highly recommended as there are number of security issues fixed. Apart from cleaner code.
Dmytrii Nagirniak
@Radek, as for `array1 = []` - no particular reason. In fact better not to have it.
Dmytrii Nagirniak
+2  A: 

There are probably plenty of more concise ways of doing this. Here's the first that came to my mind, which I'm sure could be improved upon.

def compare(array1, array2, ignore)
  return false if array1.size != array2.size
  0.upto(array1.size) do |i|
    return false if !ignore.include?(i) && array1[i] != array2[i]
  end
  return true
end

Essentially, a manual array comparison. Check for same size, then check the elements one by one (but ignoring indices we are told to ignore). Break as soon as we know it's pointless to proceed.

Matchu
@Matchu: great! it works very nicely :-) just one more clarification for me to understand your code. Once the function compare hits any of the return it won't continue running the code but exits the function. Is that right?
Radek
That is correct :) I don't want to run any more checks once we know what the function will return.
Matchu
@Matchu: thank you. For both :-)
Radek
@Matchu: why 'use do/end instead of brackets'?
Radek
@Radek, I'll mind to answer. That is the common convention in Ruby: single-line blocks use `{ }`, multiline - `do end`
Dmytrii Nagirniak
thank you Dmitriy. Every day, every day one learns something new
Radek
+6  A: 

Simplest code (requires Ruby 1.8.7 or above):

def compare(array_a, array_b, ignore_list)
  array_a.zip(array_b).each_with_index.all? do |a, b, idx|
    a == b or ignore_list.include? idx
  end
end

I suspect it will be faster, too (since it uses a single zip rather than individually asking the array for each item) — though this probably isn't a case where speed matters greatly.

As an aside, almost any time I'm directly indexing an array (like some_array[i]) in Ruby rather than using a higher-order method such as map or each, I take that as a sign that I'm probably missing something in the standard library and the algorithm will probably be less efficient than the highly optimized library function.

Chuck
Good one. I overlooked `zip` in Ruby. But where does the block with 3 arguments comes from for `all?`. The code below writes `nil` for me, which means that `a` is an element, `b` is an index. `idx` is not defined:`[1,2].zip(['a', 'b']).each_with_index.all? { |a,b,idx| puts idx; true }`
Dmytrii Nagirniak
+2  A: 

How about this?

require 'enumerator'
def compare (a1, a2, i)
  a1.size == a2.size and
    ( a1.enum_for(:each_with_index).select{ |v, j| !i.include?(j)} ==
      a2.enum_for(:each_with_index).select{ |v, j| !i.include?(j)} )

end

compare([1,2,3,4,5], [1,7,6,4,5], [1,2]) #true
compare([1,2,3,4,5], [1,7,6,4,5], [1,2]) #true

Note This will work in Ruby 1.8.6. You can use Dmitriy Nagirnyak's method to optimize this further:

def compare (a1, a2, i)
  a1.size == a2.size and
    a1.enum_for(:each_with_index).all?{|v, j| a2[j] == v or i.include?(j)}
end
KandadaBoggu
wow. let me check ...
Radek
@KandadaBoggu: it gives me false but should give true ...
Radek
What is the value you need? If you want it to be false when it matches then negate the result.
KandadaBoggu
Doesn't work when the array has duplicate values. Let me fix it.
KandadaBoggu
I have fixed the logic. Take a look.
KandadaBoggu
now it gives me `undefined method enum_for for #<Array:0x2c33398> (NoMethodError)`
Radek
You need to `require 'enumerator'`. I have added it to the code.
KandadaBoggu
+1  A: 

Here's a solution for 1.9 that compares the arrays for < and > as well as ==:

#!/usr/bin/ruby1.9

# Return -1 if array1 < array2
#         0 if array1 == array2
#        +1 if array1 > array2
# ignore contains indices of elements to ignore

def compare(array1, array2, ignore)
  filter = lambda do |a|
    a.collect.with_index do |e, i|
      if ignore.include?(i)
        ''
      else
        e
      end
    end
  end
  filter[array1] <=> filter[array2]
end

array1 = ["02/28/10","Webinars","131","0","26 Feb 2010","0","3d, 8h, 49m, 18s"]
array2 = ["02/20/10","Webinars","131","9","26 Feb 2010","0","3d, 8h, 49m, 18s"]

p compare(array1, array2, [0, 3])    # => 0
p compare(array1, array2, [0])       # => -1
p compare(array1, array2, [3])       # => 1
Wayne Conrad
A: 

Here's another one, terse and highly inefficient:

def compare a1, a2, i
  [a1,a2].map { |a|
    a.values_at(*((0...a.length).to_a - i))
  }.inject(&:==)
end
Mladen Jablanović