Skip to content

Commit

Permalink
Automatically unpack Tuple when sorting arrays
Browse files Browse the repository at this point in the history
Avoid capturing the block used for sorting, allowing automatic
unpacking of Tuples.

Since the block is only required in the inner `#quicksort!` function,
this change gives some flexibility to the user allowing them write
more expressive code.

In the following example:

    a = [{"c", 3}, {"a", 1}, {"b", 2}]
    a.sort_by &.[1]
    a.sort_by { |(x, y)| y }

Both `sort_by` calls produce the same result, one being more cryptic
and the other being explicit about unpacking.

In comparison:

    a.sort_by { |x, y| y }

Produces the same results more transparently to the developer
writing the code and behaves similar to other methods that perform
automatic unpack like `#map` or `#each`.

Performance-wise, this change does introduce a speedup on synthetic
benchmarks:

          sort_by:   2.29M (± 1.03%)  1.10× slower
    sort_by (new):   2.51M (± 0.99%)       fastest

Fixes #3419
luislavena authored and Ary Borenszweig committed Nov 16, 2016
1 parent 110b555 commit 7e62375
Showing 2 changed files with 9 additions and 2 deletions.
7 changes: 7 additions & 0 deletions spec/std/array_spec.cr
Original file line number Diff line number Diff line change
@@ -1029,6 +1029,13 @@ describe "Array" do
b.should eq(["a", "foo", "hello"])
a.should_not eq(b)
end

it "unpacks tuple" do
a = [{"d", 4}, {"a", 1}, {"c", 3}, {"e", 5}, {"b", 2}]
b = a.sort_by { |x, y| y }
b.should eq([{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}, {"e", 5}])
a.should_not eq(b)
end
end

describe "sort_by!" do
4 changes: 2 additions & 2 deletions src/array.cr
Original file line number Diff line number Diff line change
@@ -1494,11 +1494,11 @@ class Array(T)
end

def sort_by(&block : T -> _)
dup.sort_by! &block
dup.sort_by! { |e| yield(e) }
end

def sort_by!(&block : T -> _)
sorted = map { |e| {e, block.call(e)} }.sort! { |x, y| x[1] <=> y[1] }
sorted = map { |e| {e, yield(e)} }.sort! { |x, y| x[1] <=> y[1] }
@size.times do |i|
@buffer[i] = sorted.to_unsafe[i][0]
end

0 comments on commit 7e62375

Please sign in to comment.