Skip to content

Instantly share code, notes, and snippets.

@melborne
Created June 17, 2012 10:17
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save melborne/2944128 to your computer and use it in GitHub Desktop.
Save melborne/2944128 to your computer and use it in GitHub Desktop.
list method implementation with `inject`
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
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