Array::uniq { block }?

"Pit Capitain" <pit@capitain.de> schrieb im Newsbeitrag news:41FCBFE5.4000405@capitain.de...

Trans schrieb:

Has anyone, could anyone, write a Ruby version of this method? I'm not
sure what its supposed to do exaclty. Id like to see source.

No problem:

  require 'set'

  class Array
    def uniq_by
      result =
      values = Set.new
      each do |elem|
        value = yield elem
        unless values.include? value
          values << value
          result << elem
        end
      end
      result
    end
  end

  p ( 0 .. 9 ).to_a.uniq_by { |x| x % 4 } # => [0, 1, 2, 3]

I bet Robert will transform this into a version using inject :wink:

Since you asked... :-))

module Enumerable
  def uniq_by
    inject({}) {|h,x| h[yield(x)] ||= x; h}.values
  end
end

a=(1..10).map {rand 10}

=> [2, 7, 4, 1, 9, 0, 0, 2, 5, 0]

a.uniq_by {|x| x%3}

=> [9, 7, 2]

Pro:
- shorter
- needs one less temp instace

Con:
- not stable, original order is not maintained

A stable version:

module Enumerable
  def uniq_by
    h = {}; inject() {|a,x| h[yield(x)] ||= a << x}
  end
end

a.uniq_by {|x| x%3}

=> [2, 7, 9]

Beautiful about this version is that it does not need the "; h" at the end of the inject block and no ".values", too... :slight_smile:

Kind regards

    robert

"Florian Gross" <flgr@ccan.de> schrieb im Newsbeitrag news:36482aF4tni5tU1@individual.net...

Pit Capitain wrote:

Trans schrieb:

Has anyone, could anyone, write a Ruby version of this method? I'm not
sure what its supposed to do exaclty. Id like to see source.

No problem:

  require 'set'

  class Array
    def uniq_by
      result =
      values = Set.new
      each do |elem|
        value = yield elem
        unless values.include? value
          values << value
          result << elem
        end
      end
      result
    end
  end

  p ( 0 .. 9 ).to_a.uniq_by { |x| x % 4 } # => [0, 1, 2, 3]

I bet Robert will transform this into a version using inject :wink:

module Enumerable
  def uniq_by()
    inject() do |state, item|
      value = yield(item)
      state.include?(value) ? state : state + [item]
    end
  end
end

Hmmm... When deciding whether to include an element or not you mix values and converted values. That's not the same as what the implementation above does.

Also (as optimization) you could use "state << item" instead of "state + [item]" because Array#<< returns self. This saves you a lot temporary 1element arrays and avoids creating new arrays all the time (Array#+ creates a new Array). :slight_smile:

Kind regards

    robert

Thanks, now an offical part of Facets (as of next release).

T

Very nice indeed.

martin

···

Robert Klemme <bob.news@gmx.net> wrote:

module Enumerable
  def uniq_by
    h = {}; inject() {|a,x| h[yield(x)] ||= a << x}
  end
end

>> a.uniq_by {|x| x%3}
=> [2, 7, 9]

Beautiful about this version is that it does not need the "; h" at the end
of the inject block and no ".values", too... :slight_smile:

Robert Klemme wrote:

Also (as optimization) you could use "state << item" instead of "state + [item]" because Array#<< returns self. This saves you a lot temporary 1element arrays and avoids creating new arrays all the time (Array#+ creates a new Array). :slight_smile:

I dislike having the block of .inject having side effects. That sort of defeats the purpose IMHO.

Hi --

Robert Klemme wrote:

Also (as optimization) you could use "state << item" instead of "state + [item]" because Array#<< returns self. This saves you a lot temporary 1element arrays and avoids creating new arrays all the time (Array#+ creates a new Array). :slight_smile:

I dislike having the block of .inject having side effects. That sort of defeats the purpose IMHO.

I don't think it's a side effect, since it's just injecting something
into the accumulator or "injectee". This might be exactly what you
want if, for example, you have multiple references to the accumulator:

   x =
   y = x

   ["a","b","c"].inject(x) {|m,n| m << n.upcase}

   puts y # ["A", "B", "C"]

Otherwise in a case like this you could just use #map.

David

···

On Tue, 1 Feb 2005, Florian Gross wrote:

--
David A. Black
dblack@wobblini.net