3

This is not a problem, but more of a question if there are other ways to do this:

I have an array:

arr = [1,2,3,4,4,5,1,4,3]

I want this output: [2,5]

My code looks like this:

arr.select { |e| arr.count(e) == 1}

Are there some alternatives to this?

3
  • 3
    If you have two question ask them in two separate posts please. Your second question is already answered by the way. Commented Mar 13, 2014 at 12:22
  • Thanks I have deleted the other question. Commented Mar 13, 2014 at 12:28
  • @bjhaid OP wants non uniq elements... Commented Mar 13, 2014 at 12:57

3 Answers 3

10

Try this

arr.group_by { |e| e }.select { |k, v| v.size.eql? 1 }.keys
 => [2, 5] 
Sign up to request clarification or add additional context in comments.

7 Comments

Instead of the collect(&:first) you could just use keys.
In terms of readability this is definitely not better than what he has already.
@Mischa the primary problem with the OP's solution is not readability but a the full array scan for each element, O(n^2) performance.
I have tested both the solutions on benchmark for performance, mine is almost 7 times faster. count method is taking much more time to execute than how much size method is taking.
a.group_by { |e| e } (here => {1=>[1, 1], 2=>[2], 3=>[3, 3], 4=>[4, 4, 4], 5=>[5]}) will take quite some memory if the elements occur often.
|
4

Your code will scan the array once per element which is acceptable for small arrays, but it should not be necessary. By looking at this blog post it is easy to come up with

arr = [1,2,3,4,4,5,1,4,3]
counts=Hash.new(0)

arr.each do |el|
  counts[el]+=1
end

counts.select do |key, count|
  count == 1
end.keys

which yields the same result but only traverses your array once (at the expense of two additional hashes though (which you can reduce to one by using select! if you do not need the counts :-).

2 Comments

Thanks @bjhaid, did not think of that, of course you would go with select! in the one liner ;-)
arr.each_with_object(Hash.new(0)) { |x,h| h[x] += 1 }.select { |k,v| v == 1 }.keys is the one-liner :)
4

One needs to have a look at the whole array first to decide the uniqueness

def one(a)
  o = { }
  a.each do |x|
    v = o[x]
    if v == nil
      o[x] = true
    else
      if v
        o[x] = false
      end
    end
  end
  return o
end

and then use this to pick the unique elements

def unique(a)
  o = one(a)
  b = [ ]
  o.each do |k, v|
    if v
      b.push(k)
    end
  end
  return b
end

Test code

a = [ 1, 2, 3, 4, 4, 5, 1, 4, 3 ]
b = unique(a)
puts "unique: #{a} -> #{b}"

Output

unique: [1, 2, 3, 4, 4, 5, 1, 4, 3] -> [2, 5]

Dedicated to Edsger W. Dijkstra

A modern, competent programmer should not be puzzle-minded, he should not revel in tricks, he should be humble and avoid clever solutions like the plague

(from EWD303)

1 Comment

I updated it after realizing that there is no need to count over 2, thus keeping the counts small, even if we would have high repetitions. Would you be so nice to update that benchmark?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.