Skip to content

Commit db19cde

Browse files
author
José Valim
committed
Add Stream.uniq/2
1 parent 2d7965b commit db19cde

File tree

4 files changed

+50
-14
lines changed

4 files changed

+50
-14
lines changed

lib/elixir/lib/enum.ex

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,7 @@ defmodule Enum do
15201520
end
15211521

15221522
@doc """
1523-
Iterates the enumerable, removing all duplicated items.
1523+
Enumerates the collection, removing all duplicated items.
15241524
15251525
## Examples
15261526
@@ -1540,16 +1540,8 @@ defmodule Enum do
15401540
end
15411541

15421542
def uniq(collection, fun) do
1543-
{ list, _ } =
1544-
reduce(collection, { [], [] }, fn(entry, { acc, fun_acc }) ->
1545-
fun_entry = fun.(entry)
1546-
if :lists.member(fun_entry, fun_acc) do
1547-
{ acc, fun_acc }
1548-
else
1549-
{ [entry|acc], [fun_entry|fun_acc] }
1550-
end
1551-
end)
1552-
1543+
{ _, { list, _ } } =
1544+
Enumerable.reduce(collection, { :cont, { [], [] } }, R.uniq(fun))
15531545
:lists.reverse(list)
15541546
end
15551547

lib/elixir/lib/stream.ex

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,29 @@ defmodule Stream do
544544
lazy enum, fn(f1) -> R.take_while(fun, f1) end
545545
end
546546

547+
@doc """
548+
Creates a stream that only emits elements if they are uniq.
549+
550+
Keep in mind that, in order to know if an element is unique
551+
or not, this function needs to store all uniq values emitted
552+
by the stream. Therefore, if the stream is infinite, the number
553+
of items stored will grow infinitely, never being garbage collected.
554+
555+
## Examples
556+
557+
iex> Stream.uniq([1, 2, 3, 2, 1]) |> Enum.to_list
558+
[1, 2, 3]
559+
560+
iex> Stream.uniq([{1, :x}, {2, :y}, {1, :z}], fn {x, _} -> x end) |> Enum.to_list
561+
[{1,:x}, {2,:y}]
562+
563+
"""
564+
@spec uniq(Enumerable.t) :: Enumerable.t
565+
@spec uniq(Enumerable.t, (element -> term)) :: Enumerable.t
566+
def uniq(enum, fun // fn x -> x end) do
567+
lazy enum, [], fn f1 -> R.uniq(fun, f1) end
568+
end
569+
547570
@doc """
548571
Creates a stream where each item in the enumerable will
549572
be accompanied by its index.

lib/elixir/lib/stream/reducers.ex

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,18 @@ defmodule Stream.Reducers do
2525
end
2626
end
2727

28-
defmacro chunks_by(fun, f // nil) do
28+
defmacro chunks_by(callback, f // nil) do
2929
quote do
3030
fn
3131
entry, acc(h, { buffer, value }, t) ->
32-
new_value = unquote(fun).(entry)
32+
new_value = unquote(callback).(entry)
3333
if new_value == value do
3434
{ :cont, acc(h, { [entry|buffer], value }, t) }
3535
else
3636
cont_with_acc(unquote(f), :lists.reverse(buffer), h, { [entry], new_value }, t)
3737
end
3838
entry, acc(h, nil, t) ->
39-
{ :cont, acc(h, { [entry], unquote(fun).(entry) }, t) }
39+
{ :cont, acc(h, { [entry], unquote(callback).(entry) }, t) }
4040
end
4141
end
4242
end
@@ -144,6 +144,19 @@ defmodule Stream.Reducers do
144144
end
145145
end
146146

147+
defmacro uniq(callback, f // nil) do
148+
quote do
149+
fn(entry, acc(h, prev, t) = acc) ->
150+
value = unquote(callback).(entry)
151+
if :lists.member(value, prev) do
152+
{ :cont, acc }
153+
else
154+
cont_with_acc(unquote(f), entry, h, [value|prev], t)
155+
end
156+
end
157+
end
158+
end
159+
147160
defmacro with_index(f // nil) do
148161
quote do
149162
fn(entry, acc(h, counter, t)) ->

lib/elixir/test/elixir/stream_test.exs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,14 @@ defmodule StreamTest do
366366
assert Enum.zip(list, list) == Enum.zip(stream, stream)
367367
end
368368

369+
test "uniq" do
370+
assert Stream.uniq([1, 2, 3, 2, 1]) |> Enum.to_list ==
371+
[1, 2, 3]
372+
373+
assert Stream.uniq([{1, :x}, {2, :y}, {1, :z}], fn {x, _} -> x end) |> Enum.to_list ==
374+
[{1,:x}, {2,:y}]
375+
end
376+
369377
test "zip/2" do
370378
concat = Stream.concat(1..3, 4..6)
371379
cycle = Stream.cycle([:a, :b, :c])

0 commit comments

Comments
 (0)