lib/ruby/array.rb
# frozen_string_literal: true
module Stupidedi
module Refinements
refine Array do
def blank?
empty?
end
def present?
not empty?
end
# Return the first item. Raises an `IndexError` if the Array is `empty?`.
#
# @example
# [1, 2, 3].head #=> 1
#
def head
raise IndexError, "head of empty array" if empty?
x, = self
x
end
# True if `#at` is defined for the given `n`
#
# @example
# [1, 2, 3].defined_at?(0) #=> true
# [].defined_at?(0) #=> false
#
def defined_at?(n)
n < length and -n <= length
end
# @group Selection
#############################################################################
# Selects all elements except the first.
#
# @example
# [1, 2, 3].tail #=> [2, 3]
# [1].tail #=> []
# [].tail #=> []
#
# @return [Array]
def tail
_, *xs = self
xs
end
# Selects all elements except the last `n` ones.
#
# @example
# [1, 2, 3].init #=> [1, 2]
# [1, 2, 3].init(2) #=> [1]
# [].tail #=> []
#
# @return [Array]
def init(n = 1)
raise ArgumentError, "n cannot be negative" if n < 0
slice(0..-(n + 1)) or []
end
# Select all elements except the first `n` ones.
#
# @example
# [1, 2, 3].drop(1) #=> [2, 3]
# [1, 3, 3].drop(2) #=> [3]
# [].drop(10) #=> []
#
# @return [Array]
def drop(n)
raise ArgumentError, "n cannot be negative" if n < 0
slice(n..-1) or []
end
# Select the first `n` elements.
#
# @example
# [1, 2, 3].take(2) #=> [1, 2]
# [1, 2, 3].take(0) #=> []
#
# @return [Array]
def take(n)
raise ArgumentError, "n cannot be negative" if n < 0
slice(0, n) or []
end
# Split the array in two at the given position.
#
# @example
# [1, 2, 3].split_at(2) #=> [[1,2], [3]]
# [1, 2, 3].split_at(0) #=> [[], [1,2,3]]
#
# @return [(Array, Array)]
def split_at(n)
n = length + n if n < 0
return take(n), drop(n)
end
# @endgroup
#############################################################################
# @group Filtering
#############################################################################
# Drops the longest prefix of elements that satisfy the predicate.
#
# @return [Array]
def drop_while(&block)
# This is in tail call form
if not empty? and yield(head)
tail.drop_while(&block)
else
self
end
end
# Drops the longest prefix of elements that do not satisfy the predicate.
#
# @return [Array]
def drop_until(&block)
# This is in tail call form
unless empty? or yield(head)
tail.drop_until(&block)
else
self
end
end
# Takes the longest prefix of elements that satisfy the predicate.
#
# @return [Array]
def take_while(accumulator = [], &block)
# This is in tail call form
if not empty? and yield(head)
tail.take_while(head.snoc(accumulator), &block)
else
accumulator
end
end
# Takes the longest prefix of elements that do not satisfy the predicate.
#
# @return [Array]
def take_until(accumulator = [], &block)
# This is in tail call form
unless empty? or yield(head)
tail.take_until(head.snoc(accumulator), &block)
else
accumulator
end
end
# Splits the array into prefix/suffix pair according to the predicate.
#
# @return [(Array, Array)]
def split_until(&block)
prefix = take_while(&block)
suffix = drop(prefix.length)
return prefix, suffix
end
# Splits the array into prefix/suffix pair according to the predicate.
#
# @return [(Array, Array)]
def split_when(&block)
prefix = take_until(&block)
suffix = drop(prefix.length)
return prefix, suffix
end
# Returns a list of sublists, where each sublist contains only equal
# elements. Equality is determined by a two-argument block parameter.
# The concatenation of the result is equal to the original argument.
#
# @example
# "abba".split(//).group_seq(&:==) #=> [["y"], ["a"], ["b", "b"], ["a"]]
#
# @return [[Array]]
def runs(&block)
unless empty?
as, bs = tail.split_until{|x| block.call(head, x) }
head.cons(as).cons(bs.runs(&block))
else
[]
end
end
# @endgroup
#############################################################################
# Accumulate elements using the `+` method, optionally
# transforming them first using a block
#
# @example
# ["a", "b", "cd"].sum #=> "abcd"
# ["a", "b", "cd"].sum(&:length) #=> 4
#
def sum(&block)
if block_given?
tail.inject(yield(head)){|sum,e| sum + yield(e) }
else
tail.inject(head){|sum,e| sum + e }
end
end
# Count the number of elements that satisfy the predicate
#
# @example
# ["abc", "de", "fg", "hi"].count{|s| s.length == 2 } #=> 3
# ["a", "b", "a", "c", "a"].count("a") #=> 3
# [1, 3, 5, 9, 0].count #=> 5
#
# @return [Integer]
def count(*args)
if block_given?
inject(0){|n, e| yield(e) ? n + 1 : n }
elsif args.empty?
size
else
inject(0){|n, e| e == args.first ? n + 1 : n }
end
end
end
end
end