From 87d145b8399ad871eb3cb62fb7ed081ed4ee1365 Mon Sep 17 00:00:00 2001 From: lpichler Date: Mon, 4 Feb 2019 15:27:57 +0100 Subject: [PATCH] Extract method stable_sort_by --- lib/more_core_extensions/all.rb | 1 + .../core_ext/enumerable.rb | 1 + .../core_ext/enumerable/sorting.rb | 39 +++++++++++ spec/core_ext/enumerable/sorting_spec.rb | 65 +++++++++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 lib/more_core_extensions/core_ext/enumerable.rb create mode 100644 lib/more_core_extensions/core_ext/enumerable/sorting.rb create mode 100644 spec/core_ext/enumerable/sorting_spec.rb diff --git a/lib/more_core_extensions/all.rb b/lib/more_core_extensions/all.rb index 630341d..d3272e4 100644 --- a/lib/more_core_extensions/all.rb +++ b/lib/more_core_extensions/all.rb @@ -3,6 +3,7 @@ require 'more_core_extensions/core_ext/array' require 'more_core_extensions/core_ext/benchmark' require 'more_core_extensions/core_ext/class' +require 'more_core_extensions/core_ext/enumerable' require 'more_core_extensions/core_ext/hash' require 'more_core_extensions/core_ext/math' require 'more_core_extensions/core_ext/module' diff --git a/lib/more_core_extensions/core_ext/enumerable.rb b/lib/more_core_extensions/core_ext/enumerable.rb new file mode 100644 index 0000000..e2e4fb6 --- /dev/null +++ b/lib/more_core_extensions/core_ext/enumerable.rb @@ -0,0 +1 @@ +require 'more_core_extensions/core_ext/enumerable/sorting' diff --git a/lib/more_core_extensions/core_ext/enumerable/sorting.rb b/lib/more_core_extensions/core_ext/enumerable/sorting.rb new file mode 100644 index 0000000..7858ee2 --- /dev/null +++ b/lib/more_core_extensions/core_ext/enumerable/sorting.rb @@ -0,0 +1,39 @@ +module MoreCoreExtensions + module StableSorting + def self.included(klass) + klass.class_eval do + def stable_sort_by(col_names = nil, order = nil, &block) + # stabilizer is needed because of + # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/170565 + stabilizer = 0 + nil_rows, sortable = + partition do |r| + Array(col_names).any? { |c| r[c].nil? } + end + + data_array = + if col_names + sortable.sort_by do |r| + stabilizer += 1 + [Array(col_names).map do |col| + val = r[col] + val = val.downcase if val.kind_of?(String) + val = val.to_s if val.kind_of?(FalseClass) || val.kind_of?(TrueClass) + val + end, stabilizer] + end + else + sortable.sort_by(&block) + end.to_a + + data_array += nil_rows + + data_array.reverse! if order == :descending + data_array + end + end + end + end +end + +Enumerable.send(:include, MoreCoreExtensions::StableSorting) diff --git a/spec/core_ext/enumerable/sorting_spec.rb b/spec/core_ext/enumerable/sorting_spec.rb new file mode 100644 index 0000000..cf294ea --- /dev/null +++ b/spec/core_ext/enumerable/sorting_spec.rb @@ -0,0 +1,65 @@ +describe Enumerator do + let(:unsorted_enumerable) do + [ + {:id => :a, :int => 3, :string => "a", :bool => true}, + {:id => :b, :int => 1, :string => "b", :bool => true}, + {:id => :c, :int => 2, :string => "B", :bool => false}, + {:id => :d, :int => 0, :string => "b", :bool => false}, + {:id => :e, :int => 1, :string => "D", :bool => false}, + {:id => :f, :int => 0, :string => "d", :bool => nil}, + {:id => :g, :int => 1, :string => "E", :bool => true}, + {:id => :h, :int => 2, :string => "e", :bool => true}, + ].to_enum + end + + let(:int_sorted_array) do + [ + {:id => :d, :int => 0, :string => "b", :bool => false}, + {:id => :f, :int => 0, :string => "d", :bool => nil}, + {:id => :b, :int => 1, :string => "b", :bool => true}, + {:id => :e, :int => 1, :string => "D", :bool => false}, + {:id => :g, :int => 1, :string => "E", :bool => true}, + {:id => :c, :int => 2, :string => "B", :bool => false}, + {:id => :h, :int => 2, :string => "e", :bool => true}, + {:id => :a, :int => 3, :string => "a", :bool => true}, + ] + end + + let(:string_sorted_array) do + [ + {:id => :a, :int => 3, :string => "a", :bool => true}, + {:id => :b, :int => 1, :string => "b", :bool => true}, + {:id => :c, :int => 2, :string => "B", :bool => false}, + {:id => :d, :int => 0, :string => "b", :bool => false}, + {:id => :e, :int => 1, :string => "D", :bool => false}, + {:id => :f, :int => 0, :string => "d", :bool => nil}, + {:id => :g, :int => 1, :string => "E", :bool => true}, + {:id => :h, :int => 2, :string => "e", :bool => true}, + ] + end + + let(:bool_sorted_array) do + [ + {:id => :c, :int => 2, :string => "B", :bool => false}, + {:id => :d, :int => 0, :string => "b", :bool => false}, + {:id => :e, :int => 1, :string => "D", :bool => false}, + {:id => :a, :int => 3, :string => "a", :bool => true}, + {:id => :b, :int => 1, :string => "b", :bool => true}, + {:id => :g, :int => 1, :string => "E", :bool => true}, + {:id => :h, :int => 2, :string => "e", :bool => true}, + {:id => :f, :int => 0, :string => "d", :bool => nil}, + ] + end + + it "sorts stable when order is ascending" do + expect(unsorted_enumerable.stable_sort_by([:int])).to eq(int_sorted_array) + expect(unsorted_enumerable.stable_sort_by([:string])).to eq(string_sorted_array) + expect(unsorted_enumerable.stable_sort_by([:bool])).to eq(bool_sorted_array) + end + + it "sorts stable when order is descending" do + expect(unsorted_enumerable.stable_sort_by([:int], :descending)).to eq(int_sorted_array.reverse) + expect(unsorted_enumerable.stable_sort_by([:string], :descending)).to eq(string_sorted_array.reverse) + expect(unsorted_enumerable.stable_sort_by([:bool], :descending)).to eq(bool_sorted_array.reverse) + end +end