Created
June 17, 2012 10:17
-
-
Save melborne/2944128 to your computer and use it in GitHub Desktop.
list method implementation with `inject`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class List < Array | |
undef_method *%w(inject map size at index select reject detect all? any? one? none? min max minmax take_while grep include? partition group_by count join assoc zip reverse values_at compact take flat_map product) # !> `*' interpreted as argument prefix | |
def inject(init, &blk) | |
return init if empty? | |
(drop 1).inject( yield(init, first), &blk) | |
end | |
def map | |
inject([]) { |m, x| m << yield(x) } | |
end | |
def size | |
inject(0) { |m, x| m + 1 } | |
end | |
def at(pos) | |
inject(0) do |m, x| | |
return x if m==pos | |
m + 1 | |
end | |
nil | |
end | |
def index(val=nil) | |
inject(0) do |m, x| | |
return m if (val.nil? && block_given? ? yield(x) : x==val) | |
m + 1 | |
end | |
nil | |
end | |
def select | |
inject([]) { |m, x| m << x if yield(x); m } | |
end | |
def reject | |
inject([]) { |m, x| m << x unless yield(x); m } | |
end | |
def detect | |
inject(nil) do |m, x| | |
return x if yield(x) | |
m | |
end | |
end | |
def all? | |
inject(true) { |m, x| m && yield(x) } | |
end | |
def any? | |
inject(false) { |m, x| m || yield(x) } | |
end | |
def one? | |
inject(0) { |m, x| yield(x) ? m + 1 : m } == 1 | |
end | |
def none? | |
inject(0) { |m, x| yield(x) ? m + 1 : m } == 0 | |
end | |
def min | |
inject(first) do |m, x| | |
_m, _x = block_given? ? [yield(m), yield(x)] : [m, x] | |
m = x if _m > _x | |
m | |
end | |
end | |
def max | |
inject(first) do |m, x| | |
_m, _x = block_given? ? [yield(m), yield(x)] : [m, x] | |
m = x if _m < _x | |
m | |
end | |
end | |
def minmax(&blk) | |
[min(&blk), max(&blk)] | |
end | |
def take_while | |
inject([]) do |m, x| | |
return m unless yield(x) | |
m << x | |
end | |
end | |
def grep(pattern) | |
inject([]) do |m, x| | |
case x | |
when pattern | |
m << ( block_given? ? yield(x) : x ) | |
else | |
m | |
end | |
end | |
end | |
def include?(val) | |
inject(false) do |m, x| | |
return true if x == val | |
false | |
end | |
end | |
def partition | |
inject([[], []]) { |m, x| ( yield(x) ? m[0] : m[1] ) << x; m } | |
end | |
def group_by | |
inject({}) { |m, x| ( m[yield(x)] ||= [] ) << x; m } | |
end | |
def count(val=nil) | |
inject(0) do |m, x| | |
case | |
when val | |
x==val ? m + 1 : m | |
when val.nil? && block_given? | |
yield(x) ? m + 1 : m | |
else | |
m + 1 | |
end | |
end | |
end | |
def join(sep=nil) | |
s = inject('') { |m, x| m + (block_given? ? yield("#{x}#{sep}") : "#{x}#{sep}") } | |
sep ? s.gsub(/#{sep}$/,'') : s | |
end | |
def assoc(key) | |
inject(nil) do |m, x| | |
return x if x[0]==key | |
m | |
end | |
end | |
def zip(*list) | |
xs = list.map(&:dup) | |
inject([]) do |m, x| | |
m << [x] + xs.inject([]) { |_m, _x| _m << _x.shift; _m } | |
m | |
end | |
end | |
def reverse | |
inject([]) { |m, x| m.unshift x } | |
end | |
def values_at(*pos) | |
l = [] | |
inject(0) { |m, x| l << x if pos.include?(m); m + 1 } | |
l | |
end | |
def compact | |
inject([]) { |m, x| x ? m << x : m } | |
end | |
def take(n) | |
l = [] | |
inject(0) do |m, x| | |
return l if m >= n | |
l << x | |
m + 1 | |
end | |
l | |
end | |
def flat_map | |
inject([]) { |m, x| m + yield(x) } | |
end | |
def product(list) | |
inject([]) do |m, x| | |
m + list.inject([]) { |_m, y| _m << [x, y] } | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "rspec" | |
require_relative 'list' | |
describe List do | |
before(:each) do | |
@xs = List[*1..10] | |
end | |
describe "inject" do | |
it "return init value for empty list" do | |
List[].inject(0) { |m, x| m + x }.should eq 0 | |
end | |
it "return sum of elements" do | |
@xs.inject(0) { |m, x| m + x }.should eq 55 | |
@xs.should eq [*1..10] | |
end | |
it "return multi of elements" do | |
List[1,2,3,4].inject(1) { |m, x| m * x }.should eq 24 | |
end | |
it "return doubled list" do | |
List[1,2,3,4].inject([]) { |m, x| m << (x * 2) }.should eq List[2,4,6,8] | |
end | |
end | |
describe "map" do | |
it "return empty list for empty list" do | |
List[].map { |x| x + 1 }.should eq List[] | |
end | |
it "return doubled list" do | |
List[1,2,3,4].map { |x| x * 2 }.should eq List[2,4,6,8] | |
end | |
end | |
describe "size" do | |
it "return list size" do | |
@xs.size.should eq 10 | |
end | |
end | |
describe "at" do | |
it "return item at given index" do | |
List[:a, :b, :c, :d, :e].at(3).should eq :d | |
end | |
it "return nil if out of position" do | |
List[:a, :b, :c, :d].at(4).should be_nil | |
end | |
end | |
describe "index" do | |
it "return index for given val" do | |
List[1,2,3,3,4,4].index(3).should eq 2 | |
end | |
it "return nil if value not found" do | |
List[1,2,3,3,4,4].index(5).should eq nil | |
end | |
it "return index with block" do | |
List['ruby', 'python', 'haskell', 'perl'].index { |x| x.start_with?('p') }.should eq 1 | |
end | |
end | |
describe "select" do | |
it "return all matched items" do | |
@xs.select { |x| x.even? }.should eq List[2,4,6,8,10] | |
end | |
it "return [] for empty list" do | |
List[].select { |x| x.even? }.should be_empty | |
end | |
end | |
describe "reject" do | |
it "return [] for empty list" do | |
List[].reject { |x| x.even? }.should be_empty | |
end | |
it "return all non-matched items" do | |
@xs.reject { |x| x.even? }.should eq List[1,3,5,7,9] | |
end | |
end | |
describe "detect" do | |
it "return first matched item" do | |
@xs.detect { |x| x.%(3).zero? }.should eq 3 | |
end | |
it "return nil no matched item" do | |
@xs.detect { |x| x > 10 }.should be_nil | |
end | |
end | |
describe "all?" do | |
it "return false any element return false for given block" do | |
@xs.all? { |x| x < 8 }.should be_false | |
end | |
it "return true all element return true for given block" do | |
@xs.all? { |x| x.*(2).even? }.should be_true | |
end | |
end | |
describe "any?" do | |
it "return true any element return true for given block" do | |
@xs.any? { |x| x < 6 && x.even? }.should be_true | |
end | |
it "return false all element return false for given block" do | |
@xs.any? { |x| x > 9 && x.odd? }.should be_false | |
end | |
end | |
describe "one?" do | |
it "return true if only one element return true for given block" do | |
List['ant', 'bear', 'cat'].one? { |x| x.size == 4 }.should be_true | |
end | |
it "return false if two or more element return true for given block" do | |
@xs.one? { |x| x > 8 }.should be_false | |
end | |
end | |
describe "none?" do | |
it "return true if all elements return false for given block" do | |
@xs.none? { |x| x > 10 }.should be_true | |
end | |
it "return false if any element return true for given block" do | |
@xs.none? { |x| x.%(7).zero? }.should be_false | |
end | |
end | |
describe "min" do | |
it "return min element" do | |
List[4,2,9,7,3].min.should eq 2 | |
end | |
it "return min with block" do | |
List['hello', 'ruby', 'world'].min { |x| x.size }.should eq 'ruby' | |
end | |
end | |
describe "max" do | |
it "return max element" do | |
List[4,2,9,7,3].max.should eq 9 | |
end | |
it "return max with block" do | |
List['hello', 'ruby', 'worlds'].max { |x| x.size }.should eq 'worlds' | |
end | |
end | |
describe "minmax" do | |
it "return min and max elements" do | |
List[4,2,9,7,3].minmax.should eq List[2, 9] | |
end | |
it "return min and max with block" do | |
List['hello', 'ruby', 'worlds'].minmax { |x| x.size }.should eq ['ruby', 'worlds'] | |
end | |
end | |
describe "take_while" do | |
it "take elements while block return true" do | |
@xs.take_while { |x| x < 5 }.should eq List[1,2,3,4] | |
end | |
end | |
describe "grep" do | |
it "return matched with given pattern" do | |
List['abc', 'acc', 'ecc', 'aee'].grep(/.c./).should eq List['acc', 'ecc'] | |
end | |
it "return matched with given pattern with block" do | |
List['abc', 'acc', 'ecc', 'aee'].grep(/.c./) { |x| x.upcase }.should eq List['ACC', 'ECC'] | |
end | |
end | |
describe "include?" do | |
it "return true any element matched with given item" do | |
List['ruby', 'python', 'haskell'].include?('haskell').should be_true | |
end | |
end | |
describe "partition" do | |
it "split items with given condition" do | |
@xs.partition { |x| x.even? }.should eq List[List[2,4,6,8,10], List[1,3,5,7,9]] | |
end | |
end | |
describe "group_by" do | |
it "group items by given condition" do | |
@xs.group_by { |x| x%3 }.should == {1=>List[1, 4, 7, 10], 2=>List[2, 5, 8], 0=>List[3, 6, 9]} | |
end | |
end | |
describe "count" do | |
it "count items matched with given value" do | |
List[1,2,3,3,2,3,2,3].count(3).should eq 4 | |
end | |
it "count items with block condition" do | |
List['ruby', 'python', 'haskell', 'lisp'].count { |x| x.size==4 }.should eq 2 | |
end | |
it "count items without arg or block" do | |
List[1,2,3,3,2,3,2,3].count.should eq 8 | |
end | |
end | |
describe "join" do | |
before(:each) do | |
@jxs = List['hello', 'world', 'of', 'inject'] | |
end | |
it "join items" do | |
@jxs.join.should eq "helloworldofinject" | |
end | |
it "join items with block" do | |
@jxs.join { |x| x.capitalize }.should eq "HelloWorldOfInject" | |
end | |
it "join items with separator" do | |
@jxs.join('-') { |x| x.capitalize }.should eq "Hello-World-Of-Inject" | |
end | |
end | |
describe "assoc" do | |
it "return value with given key" do | |
List[[:a, 1], [:b, 2], [:c, 3]].assoc(:b).should eq List[:b, 2] | |
end | |
end | |
describe "zip" do | |
it "zip 2 lists" do | |
List[:a, :b, :c].zip(List[:x, :y, :z]).should eq List[List[:a, :x], List[:b, :y], List[:c, :z]] | |
end | |
it "zip 3 lists" do | |
List[:a, :b, :c].zip(List[:x, :y, :z], List[1, 2, 3]).should eq List[List[:a, :x, 1], List[:b, :y, 2], List[:c, :z, 3]] | |
end | |
end | |
describe "reverse" do | |
it "reverse list" do | |
List[1,2,3,4,5].reverse.should eq List[5,4,3,2,1] | |
end | |
end | |
describe "values_at" do | |
it "return values" do | |
@xs.values_at(2,5,6).should eq List[3, 6, 7] | |
end | |
end | |
describe "compact" do | |
it "return list without nil" do | |
List[1,2,nil,3,nil,5].compact.should eq List[1,2,3,5] | |
end | |
end | |
describe "take" do | |
it "return list with length of given val" do | |
@xs.take(4).should eq List[1,2,3,4] | |
end | |
it "return full list when val exceed list length" do | |
@xs.take(12).should eq @xs | |
end | |
end | |
describe "flat_map" do | |
it "concat elements" do | |
List[List[1,2],List[3,4]].flat_map { |xs| xs.map { |x| x*2 } }.should eq List[2, 4, 6, 8] | |
end | |
end | |
describe "product" do | |
it "return all combinations" do | |
List[1,2,3].product(List[4,5]).should eq [[1,4],[1,5],[2,4],[2,5],[3,4],[3,5]] | |
end | |
end | |
after(:each) do | |
@xs.should eq [*1..10] | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment