0
@search_results = Array.new
duplicates = Set.new
results.each { |result|    @search_results.push(result) unless duplicates.add?(result[:url]) }

This piece of code is garbling the order of elements in the array @search_results. Why would inserting the same element in a set and an array change the insertion order for Array? Seems like some issue with element references. Can someone explain?

Edit 1: I am using an Array. Sorry for the earlier typo. I double checked by code and it uses Array too (there is no push method for Hash anyways)

Sanjay
  • 839
  • 3
  • 10
  • 14
  • Where does the variable "results" come from? It may be that its order is indeterminate (e.g., if it is a Hash or Set). – Chuck Feb 05 '09 at 23:48
  • Please define 'garbled': what order to you expect them to be in? How was the order changed? – Lolindrath Feb 06 '09 at 12:56
  • @chuck - results is an array. @Lolindrath - I expect the order to be the same as the order in which it is inserted. Instead, it gives a random order. – Sanjay Feb 06 '09 at 13:13

2 Answers2

2

The order of elements in a Hash is not guaranteed. You'll have to sort the keys if you want a guaranteed order.

This is supposedly fixed in Ruby 1.9 I believe.

Edit: I'm assuming your results in an Array, if its a Hash then order isn't guaranteed and you'll have to sort the keys, here's what my test looks like:

#!/usr/bin/ruby -W

require 'pp'
require 'set'

results = Array.new

results << {:url => 'http://lifehacker.com'}
results << {:url => 'http://stackoverflow.com'}
results << {:url => 'http://43folders.com'}
results << {:url => 'http://lolindrath.com'}
results << {:url => 'http://stackoverflow.com'}
results << {:url => 'http://lifehacker.com'}



@search_results = Array.new
duplicates = Set.new

results.each { |result| @search_results.push(result) unless duplicates.add?(result[:url])}

puts "## @search_results"
pp @search_results

If I run that, here's the result:

## @search_results
[{:url=>"http://stackoverflow.com"}, {:url=>"http://lifehacker.com"}]

I found that odd, so just to be sure, I put a .nil? add the end of .add? and here was my result:

## @search_results
[{:url=>"http://lifehacker.com"},
 {:url=>"http://stackoverflow.com"},
 {:url=>"http://43folders.com"},
 {:url=>"http://lolindrath.com"}]

Now that was what I was expecting: is this what you mean by "garbled"?

Edit 2: Upon further investigation, I think this is because of Ruby's super strict rules when converting non-Boolean data to Booleans (see Ruby Gotchas on Wikipedia and Stack Overflow, of course) so that basically anything that only false is really false and everything else is true. so the .nil? is converting it explicitly to true/false.

irb(main):007:0> puts "zero is true" if 0
zero is true
=> nil
irb(main):008:0> puts "zero is false" unless 0
=> nil
Community
  • 1
  • 1
Lolindrath
  • 2,101
  • 14
  • 20
  • Yeah thats exactly what I meant. Pretty strange that adding .nil? makes it work. – Sanjay Feb 06 '09 at 13:11
  • The issue was that .add? returns nil on SUCCESS and the object on FAILURE, so you were testing the revers condition, since nil is falsey in Ruby – rampion Feb 06 '09 at 16:41
  • that incorrect, it returns nil if the object is already in the set - http://www.ruby-doc.org/core/classes/Set.html#M001630 – Lolindrath Feb 06 '09 at 17:58
0

Garbled how? What kind of object is results? If results is a Set or a Hash, then you're not guaranteed that any two traversals of results will be in the same order.

Also, you could do

@search_results = results.uniq

if results is an Array to get all the unique results.

------------------------------------------------------------- Array#uniq
     array.uniq   -> an_array
------------------------------------------------------------------------
     Returns a new array by removing duplicate values in self.

        a = [ "a", "a", "b", "b", "c" ]
        a.uniq   #=> ["a", "b", "c"]
rampion
  • 87,131
  • 49
  • 199
  • 315