first commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
MRuby::Gem::Specification.new('mruby-array-ext') do |spec|
|
||||
spec.license = 'MIT'
|
||||
spec.author = 'mruby developers'
|
||||
spec.summary = 'Array class extension'
|
||||
end
|
||||
@@ -0,0 +1,946 @@
|
||||
class Array
|
||||
##
|
||||
# call-seq:
|
||||
# ary.uniq! -> ary or nil
|
||||
# ary.uniq! { |item| ... } -> ary or nil
|
||||
#
|
||||
# Removes duplicate elements from +self+.
|
||||
# Returns <code>nil</code> if no changes are made (that is, no
|
||||
# duplicates are found).
|
||||
#
|
||||
# a = [ "a", "a", "b", "b", "c" ]
|
||||
# a.uniq! #=> ["a", "b", "c"]
|
||||
# b = [ "a", "b", "c" ]
|
||||
# b.uniq! #=> nil
|
||||
# c = [["student","sam"], ["student","george"], ["teacher","matz"]]
|
||||
# c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
|
||||
#
|
||||
def uniq!(&block)
|
||||
hash = {}
|
||||
if block
|
||||
self.each do |val|
|
||||
key = block.call(val)
|
||||
hash[key] = val unless hash.key?(key)
|
||||
end
|
||||
result = hash.values
|
||||
else
|
||||
hash = {}
|
||||
self.each do |val|
|
||||
hash[val] = val
|
||||
end
|
||||
result = hash.keys
|
||||
end
|
||||
if result.size == self.size
|
||||
nil
|
||||
else
|
||||
self.replace(result)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.uniq -> new_ary
|
||||
# ary.uniq { |item| ... } -> new_ary
|
||||
#
|
||||
# Returns a new array by removing duplicate values in +self+.
|
||||
#
|
||||
# a = [ "a", "a", "b", "b", "c" ]
|
||||
# a.uniq #=> ["a", "b", "c"]
|
||||
#
|
||||
# b = [["student","sam"], ["student","george"], ["teacher","matz"]]
|
||||
# b.uniq { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
|
||||
#
|
||||
def uniq(&block)
|
||||
ary = self.dup
|
||||
ary.uniq!(&block)
|
||||
ary
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary - other_ary -> new_ary
|
||||
#
|
||||
# Array Difference---Returns a new array that is a copy of
|
||||
# the original array, removing any items that also appear in
|
||||
# <i>other_ary</i>. (If you need set-like behavior, see the
|
||||
# library class Set.)
|
||||
#
|
||||
# [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ]
|
||||
#
|
||||
def -(elem)
|
||||
raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array
|
||||
|
||||
hash = {}
|
||||
array = []
|
||||
idx = 0
|
||||
len = elem.size
|
||||
while idx < len
|
||||
hash[elem[idx]] = true
|
||||
idx += 1
|
||||
end
|
||||
idx = 0
|
||||
len = size
|
||||
while idx < len
|
||||
v = self[idx]
|
||||
array << v unless hash[v]
|
||||
idx += 1
|
||||
end
|
||||
array
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.difference(other_ary1, other_ary2, ...) -> new_ary
|
||||
#
|
||||
# Returns a new array that is a copy of the original array, removing all
|
||||
# occurrences of any item that also appear in +other_ary+. The order is
|
||||
# preserved from the original array.
|
||||
#
|
||||
def difference(*args)
|
||||
ary = self
|
||||
args.each do |x|
|
||||
ary = ary - x
|
||||
end
|
||||
ary
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary | other_ary -> new_ary
|
||||
#
|
||||
# Set Union---Returns a new array by joining this array with
|
||||
# <i>other_ary</i>, removing duplicates.
|
||||
#
|
||||
# [ "a", "b", "c" ] | [ "c", "d", "a" ]
|
||||
# #=> [ "a", "b", "c", "d" ]
|
||||
#
|
||||
def |(elem)
|
||||
raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array
|
||||
|
||||
ary = self + elem
|
||||
ary.uniq! or ary
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.union(other_ary,...) -> new_ary
|
||||
#
|
||||
# Set Union---Returns a new array by joining this array with
|
||||
# <i>other_ary</i>, removing duplicates.
|
||||
#
|
||||
# ["a", "b", "c"].union(["c", "d", "a"], ["a", "c", "e"])
|
||||
# #=> ["a", "b", "c", "d", "e"]
|
||||
#
|
||||
def union(*args)
|
||||
ary = self.dup
|
||||
args.each do |x|
|
||||
ary.concat(x)
|
||||
ary.uniq!
|
||||
end
|
||||
ary
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary & other_ary -> new_ary
|
||||
#
|
||||
# Set Intersection---Returns a new array
|
||||
# containing elements common to the two arrays, with no duplicates.
|
||||
#
|
||||
# [ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ]
|
||||
#
|
||||
def &(elem)
|
||||
raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array
|
||||
|
||||
hash = {}
|
||||
array = []
|
||||
idx = 0
|
||||
len = elem.size
|
||||
while idx < len
|
||||
hash[elem[idx]] = true
|
||||
idx += 1
|
||||
end
|
||||
idx = 0
|
||||
len = size
|
||||
while idx < len
|
||||
v = self[idx]
|
||||
if hash[v]
|
||||
array << v
|
||||
hash.delete v
|
||||
end
|
||||
idx += 1
|
||||
end
|
||||
array
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.intersection(other_ary,...) -> new_ary
|
||||
#
|
||||
# Set Intersection---Returns a new array containing elements common to
|
||||
# this array and <i>other_ary</i>s, removing duplicates. The order is
|
||||
# preserved from the original array.
|
||||
#
|
||||
# [1, 2, 3].intersection([3, 4, 1], [1, 3, 5]) #=> [1, 3]
|
||||
#
|
||||
def intersection(*args)
|
||||
ary = self
|
||||
args.each do |x|
|
||||
ary = ary & x
|
||||
end
|
||||
ary
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.flatten -> new_ary
|
||||
# ary.flatten(level) -> new_ary
|
||||
#
|
||||
# Returns a new array that is a one-dimensional flattening of this
|
||||
# array (recursively). That is, for every element that is an array,
|
||||
# extract its elements into the new array. If the optional
|
||||
# <i>level</i> argument determines the level of recursion to flatten.
|
||||
#
|
||||
# s = [ 1, 2, 3 ] #=> [1, 2, 3]
|
||||
# t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
|
||||
# a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
|
||||
# a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
# a = [ 1, 2, [3, [4, 5] ] ]
|
||||
# a.flatten(1) #=> [1, 2, 3, [4, 5]]
|
||||
#
|
||||
def flatten(depth=nil)
|
||||
res = dup
|
||||
res.flatten! depth
|
||||
res
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.flatten! -> ary or nil
|
||||
# ary.flatten!(level) -> array or nil
|
||||
#
|
||||
# Flattens +self+ in place.
|
||||
# Returns <code>nil</code> if no modifications were made (i.e.,
|
||||
# <i>ary</i> contains no subarrays.) If the optional <i>level</i>
|
||||
# argument determines the level of recursion to flatten.
|
||||
#
|
||||
# a = [ 1, 2, [3, [4, 5] ] ]
|
||||
# a.flatten! #=> [1, 2, 3, 4, 5]
|
||||
# a.flatten! #=> nil
|
||||
# a #=> [1, 2, 3, 4, 5]
|
||||
# a = [ 1, 2, [3, [4, 5] ] ]
|
||||
# a.flatten!(1) #=> [1, 2, 3, [4, 5]]
|
||||
#
|
||||
def flatten!(depth=nil)
|
||||
modified = false
|
||||
ar = []
|
||||
idx = 0
|
||||
len = size
|
||||
while idx < len
|
||||
e = self[idx]
|
||||
if e.is_a?(Array) && (depth.nil? || depth > 0)
|
||||
ar += e.flatten(depth.nil? ? nil : depth - 1)
|
||||
modified = true
|
||||
else
|
||||
ar << e
|
||||
end
|
||||
idx += 1
|
||||
end
|
||||
if modified
|
||||
self.replace(ar)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.compact -> new_ary
|
||||
#
|
||||
# Returns a copy of +self+ with all +nil+ elements removed.
|
||||
#
|
||||
# [ "a", nil, "b", nil, "c", nil ].compact
|
||||
# #=> [ "a", "b", "c" ]
|
||||
#
|
||||
def compact
|
||||
result = self.dup
|
||||
result.compact!
|
||||
result
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.compact! -> ary or nil
|
||||
#
|
||||
# Removes +nil+ elements from the array.
|
||||
# Returns +nil+ if no changes were made, otherwise returns
|
||||
# <i>ary</i>.
|
||||
#
|
||||
# [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ]
|
||||
# [ "a", "b", "c" ].compact! #=> nil
|
||||
#
|
||||
def compact!
|
||||
result = self.select { |e| !e.nil? }
|
||||
if result.size == self.size
|
||||
nil
|
||||
else
|
||||
self.replace(result)
|
||||
end
|
||||
end
|
||||
|
||||
# for efficiency
|
||||
def reverse_each(&block)
|
||||
return to_enum :reverse_each unless block
|
||||
|
||||
i = self.size - 1
|
||||
while i>=0
|
||||
block.call(self[i])
|
||||
i -= 1
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.fetch(index) -> obj
|
||||
# ary.fetch(index, default) -> obj
|
||||
# ary.fetch(index) { |index| block } -> obj
|
||||
#
|
||||
# Tries to return the element at position +index+, but throws an IndexError
|
||||
# exception if the referenced +index+ lies outside of the array bounds. This
|
||||
# error can be prevented by supplying a second argument, which will act as a
|
||||
# +default+ value.
|
||||
#
|
||||
# Alternatively, if a block is given it will only be executed when an
|
||||
# invalid +index+ is referenced.
|
||||
#
|
||||
# Negative values of +index+ count from the end of the array.
|
||||
#
|
||||
# a = [ 11, 22, 33, 44 ]
|
||||
# a.fetch(1) #=> 22
|
||||
# a.fetch(-1) #=> 44
|
||||
# a.fetch(4, 'cat') #=> "cat"
|
||||
# a.fetch(100) { |i| puts "#{i} is out of bounds" }
|
||||
# #=> "100 is out of bounds"
|
||||
#
|
||||
|
||||
def fetch(n, ifnone=NONE, &block)
|
||||
warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block
|
||||
|
||||
idx = n
|
||||
if idx < 0
|
||||
idx += size
|
||||
end
|
||||
if idx < 0 || size <= idx
|
||||
return block.call(n) if block
|
||||
if ifnone == NONE
|
||||
raise IndexError, "index #{n} outside of array bounds: #{-size}...#{size}"
|
||||
end
|
||||
return ifnone
|
||||
end
|
||||
self[idx]
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.fill(obj) -> ary
|
||||
# ary.fill(obj, start [, length]) -> ary
|
||||
# ary.fill(obj, range ) -> ary
|
||||
# ary.fill { |index| block } -> ary
|
||||
# ary.fill(start [, length] ) { |index| block } -> ary
|
||||
# ary.fill(range) { |index| block } -> ary
|
||||
#
|
||||
# The first three forms set the selected elements of +self+ (which
|
||||
# may be the entire array) to +obj+.
|
||||
#
|
||||
# A +start+ of +nil+ is equivalent to zero.
|
||||
#
|
||||
# A +length+ of +nil+ is equivalent to the length of the array.
|
||||
#
|
||||
# The last three forms fill the array with the value of the given block,
|
||||
# which is passed the absolute index of each element to be filled.
|
||||
#
|
||||
# Negative values of +start+ count from the end of the array, where +-1+ is
|
||||
# the last element.
|
||||
#
|
||||
# a = [ "a", "b", "c", "d" ]
|
||||
# a.fill("x") #=> ["x", "x", "x", "x"]
|
||||
# a.fill("w", -1) #=> ["x", "x", "x", "w"]
|
||||
# a.fill("z", 2, 2) #=> ["x", "x", "z", "z"]
|
||||
# a.fill("y", 0..1) #=> ["y", "y", "z", "z"]
|
||||
# a.fill { |i| i*i } #=> [0, 1, 4, 9]
|
||||
# a.fill(-2) { |i| i*i*i } #=> [0, 1, 8, 27]
|
||||
# a.fill(1, 2) { |i| i+1 } #=> [0, 2, 3, 27]
|
||||
# a.fill(0..1) { |i| i+1 } #=> [1, 2, 3, 27]
|
||||
#
|
||||
|
||||
def fill(arg0=nil, arg1=nil, arg2=nil, &block)
|
||||
if arg0.nil? && arg1.nil? && arg2.nil? && !block
|
||||
raise ArgumentError, "wrong number of arguments (0 for 1..3)"
|
||||
end
|
||||
|
||||
beg = len = 0
|
||||
ary = []
|
||||
if block
|
||||
if arg0.nil? && arg1.nil? && arg2.nil?
|
||||
# ary.fill { |index| block } -> ary
|
||||
beg = 0
|
||||
len = self.size
|
||||
elsif !arg0.nil? && arg0.kind_of?(Range)
|
||||
# ary.fill(range) { |index| block } -> ary
|
||||
beg = arg0.begin
|
||||
beg += self.size if beg < 0
|
||||
len = arg0.end
|
||||
len += self.size if len < 0
|
||||
len += 1 unless arg0.exclude_end?
|
||||
elsif !arg0.nil?
|
||||
# ary.fill(start [, length] ) { |index| block } -> ary
|
||||
beg = arg0
|
||||
beg += self.size if beg < 0
|
||||
if arg1.nil?
|
||||
len = self.size
|
||||
else
|
||||
len = arg0 + arg1
|
||||
end
|
||||
end
|
||||
else
|
||||
if !arg0.nil? && arg1.nil? && arg2.nil?
|
||||
# ary.fill(obj) -> ary
|
||||
beg = 0
|
||||
len = self.size
|
||||
elsif !arg0.nil? && !arg1.nil? && arg1.kind_of?(Range)
|
||||
# ary.fill(obj, range ) -> ary
|
||||
beg = arg1.begin
|
||||
beg += self.size if beg < 0
|
||||
len = arg1.end
|
||||
len += self.size if len < 0
|
||||
len += 1 unless arg1.exclude_end?
|
||||
elsif !arg0.nil? && !arg1.nil?
|
||||
# ary.fill(obj, start [, length]) -> ary
|
||||
beg = arg1
|
||||
beg += self.size if beg < 0
|
||||
if arg2.nil?
|
||||
len = self.size
|
||||
else
|
||||
len = beg + arg2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
i = beg
|
||||
if block
|
||||
while i < len
|
||||
self[i] = block.call(i)
|
||||
i += 1
|
||||
end
|
||||
else
|
||||
while i < len
|
||||
self[i] = arg0
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.rotate(count=1) -> new_ary
|
||||
#
|
||||
# Returns a new array by rotating +self+ so that the element at +count+ is
|
||||
# the first element of the new array.
|
||||
#
|
||||
# If +count+ is negative then it rotates in the opposite direction, starting
|
||||
# from the end of +self+ where +-1+ is the last element.
|
||||
#
|
||||
# a = [ "a", "b", "c", "d" ]
|
||||
# a.rotate #=> ["b", "c", "d", "a"]
|
||||
# a #=> ["a", "b", "c", "d"]
|
||||
# a.rotate(2) #=> ["c", "d", "a", "b"]
|
||||
# a.rotate(-3) #=> ["b", "c", "d", "a"]
|
||||
|
||||
def rotate(count=1)
|
||||
ary = []
|
||||
len = self.length
|
||||
|
||||
if len > 0
|
||||
idx = (count < 0) ? (len - (~count % len) - 1) : (count % len) # rotate count
|
||||
len.times do
|
||||
ary << self[idx]
|
||||
idx += 1
|
||||
idx = 0 if idx > len-1
|
||||
end
|
||||
end
|
||||
ary
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.rotate!(count=1) -> ary
|
||||
#
|
||||
# Rotates +self+ in place so that the element at +count+ comes first, and
|
||||
# returns +self+.
|
||||
#
|
||||
# If +count+ is negative then it rotates in the opposite direction, starting
|
||||
# from the end of the array where +-1+ is the last element.
|
||||
#
|
||||
# a = [ "a", "b", "c", "d" ]
|
||||
# a.rotate! #=> ["b", "c", "d", "a"]
|
||||
# a #=> ["b", "c", "d", "a"]
|
||||
# a.rotate!(2) #=> ["d", "a", "b", "c"]
|
||||
# a.rotate!(-3) #=> ["a", "b", "c", "d"]
|
||||
|
||||
def rotate!(count=1)
|
||||
self.replace(self.rotate(count))
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.delete_if { |item| block } -> ary
|
||||
# ary.delete_if -> Enumerator
|
||||
#
|
||||
# Deletes every element of +self+ for which block evaluates to +true+.
|
||||
#
|
||||
# The array is changed instantly every time the block is called, not after
|
||||
# the iteration is over.
|
||||
#
|
||||
# See also Array#reject!
|
||||
#
|
||||
# If no block is given, an Enumerator is returned instead.
|
||||
#
|
||||
# scores = [ 97, 42, 75 ]
|
||||
# scores.delete_if {|score| score < 80 } #=> [97]
|
||||
|
||||
def delete_if(&block)
|
||||
return to_enum :delete_if unless block
|
||||
|
||||
idx = 0
|
||||
while idx < self.size do
|
||||
if block.call(self[idx])
|
||||
self.delete_at(idx)
|
||||
else
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.reject! { |item| block } -> ary or nil
|
||||
# ary.reject! -> Enumerator
|
||||
#
|
||||
# Equivalent to Array#delete_if, deleting elements from +self+ for which the
|
||||
# block evaluates to +true+, but returns +nil+ if no changes were made.
|
||||
#
|
||||
# The array is changed instantly every time the block is called, not after
|
||||
# the iteration is over.
|
||||
#
|
||||
# See also Enumerable#reject and Array#delete_if.
|
||||
#
|
||||
# If no block is given, an Enumerator is returned instead.
|
||||
|
||||
def reject!(&block)
|
||||
return to_enum :reject! unless block
|
||||
|
||||
len = self.size
|
||||
idx = 0
|
||||
while idx < self.size do
|
||||
if block.call(self[idx])
|
||||
self.delete_at(idx)
|
||||
else
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
if self.size == len
|
||||
nil
|
||||
else
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.insert(index, obj...) -> ary
|
||||
#
|
||||
# Inserts the given values before the element with the given +index+.
|
||||
#
|
||||
# Negative indices count backwards from the end of the array, where +-1+ is
|
||||
# the last element.
|
||||
#
|
||||
# a = %w{ a b c d }
|
||||
# a.insert(2, 99) #=> ["a", "b", 99, "c", "d"]
|
||||
# a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"]
|
||||
|
||||
def insert(idx, *args)
|
||||
idx += self.size + 1 if idx < 0
|
||||
self[idx, 0] = args
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.bsearch {|x| block } -> elem
|
||||
#
|
||||
# By using binary search, finds a value from this array which meets
|
||||
# the given condition in O(log n) where n is the size of the array.
|
||||
#
|
||||
# You can use this method in two use cases: a find-minimum mode and
|
||||
# a find-any mode. In either case, the elements of the array must be
|
||||
# monotone (or sorted) with respect to the block.
|
||||
#
|
||||
# In find-minimum mode (this is a good choice for typical use case),
|
||||
# the block must return true or false, and there must be an index i
|
||||
# (0 <= i <= ary.size) so that:
|
||||
#
|
||||
# - the block returns false for any element whose index is less than
|
||||
# i, and
|
||||
# - the block returns true for any element whose index is greater
|
||||
# than or equal to i.
|
||||
#
|
||||
# This method returns the i-th element. If i is equal to ary.size,
|
||||
# it returns nil.
|
||||
#
|
||||
# ary = [0, 4, 7, 10, 12]
|
||||
# ary.bsearch {|x| x >= 4 } #=> 4
|
||||
# ary.bsearch {|x| x >= 6 } #=> 7
|
||||
# ary.bsearch {|x| x >= -1 } #=> 0
|
||||
# ary.bsearch {|x| x >= 100 } #=> nil
|
||||
#
|
||||
# In find-any mode (this behaves like libc's bsearch(3)), the block
|
||||
# must return a number, and there must be two indices i and j
|
||||
# (0 <= i <= j <= ary.size) so that:
|
||||
#
|
||||
# - the block returns a positive number for ary[k] if 0 <= k < i,
|
||||
# - the block returns zero for ary[k] if i <= k < j, and
|
||||
# - the block returns a negative number for ary[k] if
|
||||
# j <= k < ary.size.
|
||||
#
|
||||
# Under this condition, this method returns any element whose index
|
||||
# is within i...j. If i is equal to j (i.e., there is no element
|
||||
# that satisfies the block), this method returns nil.
|
||||
#
|
||||
# ary = [0, 4, 7, 10, 12]
|
||||
# # try to find v such that 4 <= v < 8
|
||||
# ary.bsearch {|x| 1 - (x / 4).truncate } #=> 4 or 7
|
||||
# # try to find v such that 8 <= v < 10
|
||||
# ary.bsearch {|x| 4 - (x / 2).truncate } #=> nil
|
||||
#
|
||||
# You must not mix the two modes at a time; the block must always
|
||||
# return either true/false, or always return a number. It is
|
||||
# undefined which value is actually picked up at each iteration.
|
||||
|
||||
def bsearch(&block)
|
||||
return to_enum :bsearch unless block
|
||||
|
||||
if idx = bsearch_index(&block)
|
||||
self[idx]
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.bsearch_index {|x| block } -> int or nil
|
||||
#
|
||||
# By using binary search, finds an index of a value from this array which
|
||||
# meets the given condition in O(log n) where n is the size of the array.
|
||||
#
|
||||
# It supports two modes, depending on the nature of the block and they are
|
||||
# exactly the same as in the case of #bsearch method with the only difference
|
||||
# being that this method returns the index of the element instead of the
|
||||
# element itself. For more details consult the documentation for #bsearch.
|
||||
|
||||
def bsearch_index(&block)
|
||||
return to_enum :bsearch_index unless block
|
||||
|
||||
low = 0
|
||||
high = size
|
||||
satisfied = false
|
||||
|
||||
while low < high
|
||||
mid = ((low+high)/2).truncate
|
||||
res = block.call self[mid]
|
||||
|
||||
case res
|
||||
when 0 # find-any mode: Found!
|
||||
return mid
|
||||
when Numeric # find-any mode: Continue...
|
||||
in_lower_half = res < 0
|
||||
when true # find-min mode
|
||||
in_lower_half = true
|
||||
satisfied = true
|
||||
when false, nil # find-min mode
|
||||
in_lower_half = false
|
||||
else
|
||||
raise TypeError, 'invalid block result (must be numeric, true, false or nil)'
|
||||
end
|
||||
|
||||
if in_lower_half
|
||||
high = mid
|
||||
else
|
||||
low = mid + 1
|
||||
end
|
||||
end
|
||||
|
||||
satisfied ? low : nil
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.keep_if { |item| block } -> ary
|
||||
# ary.keep_if -> Enumerator
|
||||
#
|
||||
# Deletes every element of +self+ for which the given block evaluates to
|
||||
# +false+.
|
||||
#
|
||||
# See also Array#select!
|
||||
#
|
||||
# If no block is given, an Enumerator is returned instead.
|
||||
#
|
||||
# a = [1, 2, 3, 4, 5]
|
||||
# a.keep_if { |val| val > 3 } #=> [4, 5]
|
||||
|
||||
def keep_if(&block)
|
||||
return to_enum :keep_if unless block
|
||||
|
||||
idx = 0
|
||||
len = self.size
|
||||
while idx < self.size do
|
||||
if block.call(self[idx])
|
||||
idx += 1
|
||||
else
|
||||
self.delete_at(idx)
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.select! {|item| block } -> ary or nil
|
||||
# ary.select! -> Enumerator
|
||||
#
|
||||
# Invokes the given block passing in successive elements from +self+,
|
||||
# deleting elements for which the block returns a +false+ value.
|
||||
#
|
||||
# If changes were made, it will return +self+, otherwise it returns +nil+.
|
||||
#
|
||||
# See also Array#keep_if
|
||||
#
|
||||
# If no block is given, an Enumerator is returned instead.
|
||||
|
||||
def select!(&block)
|
||||
return to_enum :select! unless block
|
||||
|
||||
result = []
|
||||
idx = 0
|
||||
len = size
|
||||
while idx < len
|
||||
elem = self[idx]
|
||||
result << elem if block.call(elem)
|
||||
idx += 1
|
||||
end
|
||||
return nil if len == result.size
|
||||
self.replace(result)
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.index(val) -> int or nil
|
||||
# ary.index {|item| block } -> int or nil
|
||||
#
|
||||
# Returns the _index_ of the first object in +ary+ such that the object is
|
||||
# <code>==</code> to +obj+.
|
||||
#
|
||||
# If a block is given instead of an argument, returns the _index_ of the
|
||||
# first object for which the block returns +true+. Returns +nil+ if no
|
||||
# match is found.
|
||||
#
|
||||
# ISO 15.2.12.5.14
|
||||
def index(val=NONE, &block)
|
||||
return to_enum(:find_index, val) if !block && val == NONE
|
||||
|
||||
if block
|
||||
idx = 0
|
||||
len = size
|
||||
while idx < len
|
||||
return idx if block.call self[idx]
|
||||
idx += 1
|
||||
end
|
||||
else
|
||||
return self.__ary_index(val)
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.dig(idx, ...) -> object
|
||||
#
|
||||
# Extracts the nested value specified by the sequence of <i>idx</i>
|
||||
# objects by calling +dig+ at each step, returning +nil+ if any
|
||||
# intermediate step is +nil+.
|
||||
#
|
||||
def dig(idx,*args)
|
||||
n = self[idx]
|
||||
if args.size > 0
|
||||
n&.dig(*args)
|
||||
else
|
||||
n
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.permutation { |p| block } -> ary
|
||||
# ary.permutation -> Enumerator
|
||||
# ary.permutation(n) { |p| block } -> ary
|
||||
# ary.permutation(n) -> Enumerator
|
||||
#
|
||||
# When invoked with a block, yield all permutations of length +n+ of the
|
||||
# elements of the array, then return the array itself.
|
||||
#
|
||||
# If +n+ is not specified, yield all permutations of all elements.
|
||||
#
|
||||
# The implementation makes no guarantees about the order in which the
|
||||
# permutations are yielded.
|
||||
#
|
||||
# If no block is given, an Enumerator is returned instead.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# a = [1, 2, 3]
|
||||
# a.permutation.to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
|
||||
# a.permutation(1).to_a #=> [[1],[2],[3]]
|
||||
# a.permutation(2).to_a #=> [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]]
|
||||
# a.permutation(3).to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
|
||||
# a.permutation(0).to_a #=> [[]] # one permutation of length 0
|
||||
# a.permutation(4).to_a #=> [] # no permutations of length 4
|
||||
def permutation(n=self.size, &block)
|
||||
return to_enum(:permutation, n) unless block
|
||||
size = self.size
|
||||
if n == 0
|
||||
yield []
|
||||
elsif 0 < n && n <= size
|
||||
i = 0
|
||||
while i<size
|
||||
result = [self[i]]
|
||||
if n-1 > 0
|
||||
ary = self[0...i] + self[i+1..-1]
|
||||
ary.permutation(n-1) do |c|
|
||||
yield result + c
|
||||
end
|
||||
else
|
||||
yield result
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.combination(n) { |c| block } -> ary
|
||||
# ary.combination(n) -> Enumerator
|
||||
#
|
||||
# When invoked with a block, yields all combinations of length +n+ of elements
|
||||
# from the array and then returns the array itself.
|
||||
#
|
||||
# The implementation makes no guarantees about the order in which the
|
||||
# combinations are yielded.
|
||||
#
|
||||
# If no block is given, an Enumerator is returned instead.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# a = [1, 2, 3, 4]
|
||||
# a.combination(1).to_a #=> [[1],[2],[3],[4]]
|
||||
# a.combination(2).to_a #=> [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
|
||||
# a.combination(3).to_a #=> [[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
|
||||
# a.combination(4).to_a #=> [[1,2,3,4]]
|
||||
# a.combination(0).to_a #=> [[]] # one combination of length 0
|
||||
# a.combination(5).to_a #=> [] # no combinations of length 5
|
||||
|
||||
def combination(n, &block)
|
||||
return to_enum(:combination, n) unless block
|
||||
size = self.size
|
||||
if n == 0
|
||||
yield []
|
||||
elsif n == 1
|
||||
i = 0
|
||||
while i<size
|
||||
yield [self[i]]
|
||||
i += 1
|
||||
end
|
||||
elsif n <= size
|
||||
i = 0
|
||||
while i<size
|
||||
result = [self[i]]
|
||||
self[i+1..-1].combination(n-1) do |c|
|
||||
yield result + c
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.transpose -> new_ary
|
||||
#
|
||||
# Assumes that self is an array of arrays and transposes the rows and columns.
|
||||
#
|
||||
# If the length of the subarrays don't match, an IndexError is raised.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# a = [[1,2], [3,4], [5,6]]
|
||||
# a.transpose #=> [[1, 3, 5], [2, 4, 6]]
|
||||
|
||||
def transpose
|
||||
return [] if empty?
|
||||
|
||||
column_count = nil
|
||||
self.each do |row|
|
||||
raise TypeError unless row.is_a?(Array)
|
||||
column_count ||= row.size
|
||||
raise IndexError, 'element size differs' unless column_count == row.size
|
||||
end
|
||||
|
||||
Array.new(column_count) do |column_index|
|
||||
self.map { |row| row[column_index] }
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.to_h -> Hash
|
||||
# ary.to_h{|item| ... } -> Hash
|
||||
#
|
||||
# Returns the result of interpreting <i>aray</i> as an array of
|
||||
# <tt>[key, value]</tt> pairs. If a block is given, it should
|
||||
# return <tt>[key, value]</tt> pairs to construct a hash.
|
||||
#
|
||||
# [[:foo, :bar], [1, 2]].to_h
|
||||
# # => {:foo => :bar, 1 => 2}
|
||||
# [1, 2].to_h{|x| [x, x*2]}
|
||||
# # => {1 => 2, 2 => 4}
|
||||
#
|
||||
def to_h(&blk)
|
||||
h = {}
|
||||
self.each do |v|
|
||||
v = blk.call(v) if blk
|
||||
raise TypeError, "wrong element type #{v.class}" unless Array === v
|
||||
raise ArgumentError, "wrong array length (expected 2, was #{v.length})" unless v.length == 2
|
||||
h[v[0]] = v[1]
|
||||
end
|
||||
h
|
||||
end
|
||||
|
||||
alias append push
|
||||
alias prepend unshift
|
||||
alias filter! select!
|
||||
end
|
||||
@@ -0,0 +1,200 @@
|
||||
#include <mruby.h>
|
||||
#include <mruby/value.h>
|
||||
#include <mruby/array.h>
|
||||
#include <mruby/range.h>
|
||||
#include <mruby/hash.h>
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.assoc(obj) -> new_ary or nil
|
||||
*
|
||||
* Searches through an array whose elements are also arrays
|
||||
* comparing _obj_ with the first element of each contained array
|
||||
* using obj.==.
|
||||
* Returns the first contained array that matches (that
|
||||
* is, the first associated array),
|
||||
* or +nil+ if no match is found.
|
||||
* See also <code>Array#rassoc</code>.
|
||||
*
|
||||
* s1 = [ "colors", "red", "blue", "green" ]
|
||||
* s2 = [ "letters", "a", "b", "c" ]
|
||||
* s3 = "foo"
|
||||
* a = [ s1, s2, s3 ]
|
||||
* a.assoc("letters") #=> [ "letters", "a", "b", "c" ]
|
||||
* a.assoc("foo") #=> nil
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
mrb_ary_assoc(mrb_state *mrb, mrb_value ary)
|
||||
{
|
||||
mrb_int i;
|
||||
mrb_value v;
|
||||
mrb_value k = mrb_get_arg1(mrb);
|
||||
|
||||
for (i = 0; i < RARRAY_LEN(ary); ++i) {
|
||||
v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]);
|
||||
if (!mrb_nil_p(v) && RARRAY_LEN(v) > 0 &&
|
||||
mrb_equal(mrb, RARRAY_PTR(v)[0], k))
|
||||
return v;
|
||||
}
|
||||
return mrb_nil_value();
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.rassoc(obj) -> new_ary or nil
|
||||
*
|
||||
* Searches through the array whose elements are also arrays. Compares
|
||||
* _obj_ with the second element of each contained array using
|
||||
* <code>==</code>. Returns the first contained array that matches. See
|
||||
* also <code>Array#assoc</code>.
|
||||
*
|
||||
* a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ]
|
||||
* a.rassoc("two") #=> [2, "two"]
|
||||
* a.rassoc("four") #=> nil
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
mrb_ary_rassoc(mrb_state *mrb, mrb_value ary)
|
||||
{
|
||||
mrb_int i;
|
||||
mrb_value v;
|
||||
mrb_value value = mrb_get_arg1(mrb);
|
||||
|
||||
for (i = 0; i < RARRAY_LEN(ary); ++i) {
|
||||
v = RARRAY_PTR(ary)[i];
|
||||
if (mrb_array_p(v) &&
|
||||
RARRAY_LEN(v) > 1 &&
|
||||
mrb_equal(mrb, RARRAY_PTR(v)[1], value))
|
||||
return v;
|
||||
}
|
||||
return mrb_nil_value();
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.at(index) -> obj or nil
|
||||
*
|
||||
* Returns the element at _index_. A
|
||||
* negative index counts from the end of +self+. Returns +nil+
|
||||
* if the index is out of range. See also <code>Array#[]</code>.
|
||||
*
|
||||
* a = [ "a", "b", "c", "d", "e" ]
|
||||
* a.at(0) #=> "a"
|
||||
* a.at(-1) #=> "e"
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
mrb_ary_at(mrb_state *mrb, mrb_value ary)
|
||||
{
|
||||
mrb_int pos;
|
||||
mrb_get_args(mrb, "i", &pos);
|
||||
|
||||
return mrb_ary_entry(ary, pos);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_ary_values_at(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int argc;
|
||||
mrb_value *argv;
|
||||
|
||||
mrb_get_args(mrb, "*", &argv, &argc);
|
||||
|
||||
return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ary.slice!(index) -> obj or nil
|
||||
* ary.slice!(start, length) -> new_ary or nil
|
||||
* ary.slice!(range) -> new_ary or nil
|
||||
*
|
||||
* Deletes the element(s) given by an +index+ (optionally up to +length+
|
||||
* elements) or by a +range+.
|
||||
*
|
||||
* Returns the deleted object (or objects), or +nil+ if the +index+ is out of
|
||||
* range.
|
||||
*
|
||||
* a = [ "a", "b", "c" ]
|
||||
* a.slice!(1) #=> "b"
|
||||
* a #=> ["a", "c"]
|
||||
* a.slice!(-1) #=> "c"
|
||||
* a #=> ["a"]
|
||||
* a.slice!(100) #=> nil
|
||||
* a #=> ["a"]
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
struct RArray *a = mrb_ary_ptr(self);
|
||||
mrb_int i, j, k, len, alen;
|
||||
mrb_value val;
|
||||
mrb_value *ptr;
|
||||
mrb_value ary;
|
||||
|
||||
mrb_ary_modify(mrb, a);
|
||||
|
||||
if (mrb_get_argc(mrb) == 1) {
|
||||
mrb_value index = mrb_get_arg1(mrb);
|
||||
|
||||
switch (mrb_type(index)) {
|
||||
case MRB_TT_RANGE:
|
||||
if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == MRB_RANGE_OK) {
|
||||
goto delete_pos_len;
|
||||
}
|
||||
else {
|
||||
return mrb_nil_value();
|
||||
}
|
||||
case MRB_TT_FIXNUM:
|
||||
val = mrb_funcall(mrb, self, "delete_at", 1, index);
|
||||
return val;
|
||||
default:
|
||||
val = mrb_funcall(mrb, self, "delete_at", 1, index);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
mrb_get_args(mrb, "ii", &i, &len);
|
||||
delete_pos_len:
|
||||
alen = ARY_LEN(a);
|
||||
if (i < 0) i += alen;
|
||||
if (i < 0 || alen < i) return mrb_nil_value();
|
||||
if (len < 0) return mrb_nil_value();
|
||||
if (alen == i) return mrb_ary_new(mrb);
|
||||
if (len > alen - i) len = alen - i;
|
||||
|
||||
ary = mrb_ary_new_capa(mrb, len);
|
||||
ptr = ARY_PTR(a);
|
||||
for (j = i, k = 0; k < len; ++j, ++k) {
|
||||
mrb_ary_push(mrb, ary, ptr[j]);
|
||||
}
|
||||
|
||||
ptr += i;
|
||||
for (j = i; j < alen - len; ++j) {
|
||||
*ptr = *(ptr+len);
|
||||
++ptr;
|
||||
}
|
||||
|
||||
mrb_ary_resize(mrb, self, alen - len);
|
||||
return ary;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_mruby_array_ext_gem_init(mrb_state* mrb)
|
||||
{
|
||||
struct RClass * a = mrb->array_class;
|
||||
|
||||
mrb_define_method(mrb, a, "assoc", mrb_ary_assoc, MRB_ARGS_REQ(1));
|
||||
mrb_define_method(mrb, a, "at", mrb_ary_at, MRB_ARGS_REQ(1));
|
||||
mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1));
|
||||
mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY());
|
||||
mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ARG(1,1));
|
||||
}
|
||||
|
||||
void
|
||||
mrb_mruby_array_ext_gem_final(mrb_state* mrb)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
##
|
||||
# Array(Ext) Test
|
||||
|
||||
def assert_permutation_combination(exp, receiver, meth, *args)
|
||||
act = []
|
||||
ret = receiver.__send__(meth, *args) { |v| act << v }
|
||||
assert "assert_#{meth}" do
|
||||
assert_equal(exp, act.sort)
|
||||
assert_same(receiver, ret)
|
||||
end
|
||||
end
|
||||
|
||||
def assert_permutation(exp, receiver, *args)
|
||||
assert_permutation_combination(exp, receiver, :permutation, *args)
|
||||
end
|
||||
|
||||
def assert_combination(exp, receiver, *args)
|
||||
assert_permutation_combination(exp, receiver, :combination, *args)
|
||||
end
|
||||
|
||||
assert("Array#assoc") do
|
||||
s1 = [ "colors", "red", "blue", "green" ]
|
||||
s2 = [ "letters", "a", "b", "c" ]
|
||||
s3 = "foo"
|
||||
a = [ s1, s2, s3 ]
|
||||
|
||||
assert_equal [ "letters", "a", "b", "c" ], a.assoc("letters")
|
||||
assert_nil a.assoc("foo")
|
||||
end
|
||||
|
||||
assert("Array#at") do
|
||||
a = [ "a", "b", "c", "d", "e" ]
|
||||
assert_equal "a", a.at(0)
|
||||
assert_equal "e", a.at(-1)
|
||||
end
|
||||
|
||||
assert("Array#rassoc") do
|
||||
a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ]
|
||||
|
||||
assert_equal [2, "two"], a.rassoc("two")
|
||||
assert_nil a.rassoc("four")
|
||||
end
|
||||
|
||||
assert("Array#uniq!") do
|
||||
a = [1, 2, 3, 1]
|
||||
a.uniq!
|
||||
assert_equal [1, 2, 3], a
|
||||
|
||||
b = [ "a", "b", "c" ]
|
||||
assert_nil b.uniq!
|
||||
|
||||
c = [["student","sam"], ["student","george"], ["teacher","matz"]]
|
||||
assert_equal [["student", "sam"], ["teacher", "matz"]], c.uniq! { |s| s.first }
|
||||
|
||||
d = [["student","sam"], ["teacher","matz"]]
|
||||
assert_nil d.uniq! { |s| s.first }
|
||||
end
|
||||
|
||||
assert("Array#uniq") do
|
||||
a = [1, 2, 3, 1]
|
||||
assert_equal [1, 2, 3], a.uniq
|
||||
assert_equal [1, 2, 3, 1], a
|
||||
|
||||
b = [["student","sam"], ["student","george"], ["teacher","matz"]]
|
||||
assert_equal [["student", "sam"], ["teacher", "matz"]], b.uniq { |s| s.first }
|
||||
end
|
||||
|
||||
assert("Array#-") do
|
||||
a = [1, 2, 3, 1]
|
||||
b = [1]
|
||||
c = 1
|
||||
|
||||
assert_raise(TypeError) { a - c }
|
||||
assert_equal [2, 3], (a - b)
|
||||
assert_equal [1, 2, 3, 1], a
|
||||
end
|
||||
|
||||
assert("Array#|") do
|
||||
a = [1, 2, 3, 1]
|
||||
b = [1, 4]
|
||||
c = 1
|
||||
|
||||
assert_raise(TypeError) { a | c }
|
||||
assert_equal [1, 2, 3, 4], (a | b)
|
||||
assert_equal [1, 2, 3, 1], a
|
||||
end
|
||||
|
||||
assert("Array#union") do
|
||||
a = [1, 2, 3, 1]
|
||||
b = [1, 4]
|
||||
c = [1, 5]
|
||||
|
||||
assert_equal [1, 2, 3, 4, 5], a.union(b,c)
|
||||
end
|
||||
|
||||
assert("Array#difference") do
|
||||
a = [1, 2, 3, 1, 6, 7]
|
||||
b = [1, 4, 6]
|
||||
c = [1, 5, 7]
|
||||
|
||||
assert_equal [2, 3], a.difference(b,c)
|
||||
end
|
||||
|
||||
assert("Array#&") do
|
||||
a = [1, 2, 3, 1]
|
||||
b = [1, 4]
|
||||
c = 1
|
||||
|
||||
assert_raise(TypeError) { a & c }
|
||||
assert_equal [1], (a & b)
|
||||
assert_equal [1, 2, 3, 1], a
|
||||
end
|
||||
|
||||
assert("Array#intersection") do
|
||||
a = [1, 2, 3, 1, 8, 6, 7, 8]
|
||||
b = [1, 4, 6, 8]
|
||||
c = [1, 5, 7, 8]
|
||||
|
||||
assert_equal [1, 8], a.intersection(b,c)
|
||||
end
|
||||
|
||||
assert("Array#flatten") do
|
||||
assert_equal [1, 2, "3", {4=>5}, :'6'], [1, 2, "3", {4=>5}, :'6'].flatten
|
||||
assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, 4, 5], 6].flatten
|
||||
assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten
|
||||
assert_equal [1, [2, [3, [4, [5, [6]]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(0)
|
||||
assert_equal [1, 2, [3, [4, [5, [6]]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(1)
|
||||
assert_equal [1, 2, 3, [4, [5, [6]]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(2)
|
||||
assert_equal [1, 2, 3, 4, [5, [6]]], [1, [2, [3, [4, [5, [6]]]]]].flatten(3)
|
||||
assert_equal [1, 2, 3, 4, 5, [6]], [1, [2, [3, [4, [5, [6]]]]]].flatten(4)
|
||||
assert_equal [1, 2, 3, 4, 5, 6], [1, [2, [3, [4, [5, [6]]]]]].flatten(5)
|
||||
end
|
||||
|
||||
assert("Array#flatten!") do
|
||||
assert_equal [1, 2, 3, 4, 5, 6], [1, 2, [3, [4, 5], 6]].flatten!
|
||||
end
|
||||
|
||||
assert("Array#compact") do
|
||||
a = [1, nil, "2", nil, :t, false, nil]
|
||||
assert_equal [1, "2", :t, false], a.compact
|
||||
assert_equal [1, nil, "2", nil, :t, false, nil], a
|
||||
end
|
||||
|
||||
assert("Array#compact!") do
|
||||
a = [1, nil, "2", nil, :t, false, nil]
|
||||
a.compact!
|
||||
assert_equal [1, "2", :t, false], a
|
||||
end
|
||||
|
||||
assert("Array#fetch") do
|
||||
a = [ 11, 22, 33, 44 ]
|
||||
assert_equal 22, a.fetch(1)
|
||||
assert_equal 44, a.fetch(-1)
|
||||
assert_equal 'cat', a.fetch(4, 'cat')
|
||||
ret = 0
|
||||
a.fetch(100) { |i| ret = i }
|
||||
assert_equal 100, ret
|
||||
assert_raise(IndexError) { a.fetch(100) }
|
||||
end
|
||||
|
||||
assert("Array#fill") do
|
||||
a = [ "a", "b", "c", "d" ]
|
||||
assert_equal ["x", "x", "x", "x"], a.fill("x")
|
||||
assert_equal ["x", "x", "x", "w"], a.fill("w", -1)
|
||||
assert_equal ["x", "x", "z", "z"], a.fill("z", 2, 2)
|
||||
assert_equal ["y", "y", "z", "z"], a.fill("y", 0..1)
|
||||
assert_equal [0, 1, 4, 9], a.fill { |i| i*i }
|
||||
assert_equal [0, 1, 8, 27], a.fill(-2) { |i| i*i*i }
|
||||
assert_equal [0, 2, 3, 27], a.fill(1, 2) { |i| i+1 }
|
||||
assert_equal [1, 2, 3, 27], a.fill(0..1) { |i| i+1 }
|
||||
assert_raise(ArgumentError) { a.fill }
|
||||
|
||||
assert_equal([0, 1, 2, 3, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, -2, 1))
|
||||
assert_equal([0, 1, 2, 3, -1, -1, -1], [0, 1, 2, 3, 4, 5].fill(-1, -2, 3))
|
||||
assert_equal([0, 1, 2, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3..4))
|
||||
assert_equal([0, 1, 2, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 3...4))
|
||||
assert_equal([0, 1, -1, -1, -1, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2..-2))
|
||||
assert_equal([0, 1, -1, -1, 4, 5], [0, 1, 2, 3, 4, 5].fill(-1, 2...-2))
|
||||
assert_equal([0, 1, 2, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(3..4){|i| i+10})
|
||||
assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10})
|
||||
assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10})
|
||||
assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10})
|
||||
|
||||
assert_equal [1, 2, 3, 4, 'x', 'x'], [1, 2, 3, 4, 5, 6].fill('x', -2..-1)
|
||||
assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-1)
|
||||
assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-2)
|
||||
assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2..-2)
|
||||
assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2..0)
|
||||
end
|
||||
|
||||
assert("Array#reverse_each") do
|
||||
a = [ "a", "b", "c", "d" ]
|
||||
b = []
|
||||
a.reverse_each do |i|
|
||||
b << i
|
||||
end
|
||||
assert_equal [ "d", "c", "b", "a" ], b
|
||||
end
|
||||
|
||||
assert("Array#rotate") do
|
||||
a = ["a", "b", "c", "d"]
|
||||
assert_equal ["b", "c", "d", "a"], a.rotate
|
||||
assert_equal ["a", "b", "c", "d"], a
|
||||
assert_equal ["c", "d", "a", "b"], a.rotate(2)
|
||||
assert_equal ["b", "c", "d", "a"], a.rotate(-3)
|
||||
assert_equal ["c", "d", "a", "b"], a.rotate(10)
|
||||
assert_equal [], [].rotate
|
||||
end
|
||||
|
||||
assert("Array#rotate!") do
|
||||
a = ["a", "b", "c", "d"]
|
||||
assert_equal ["b", "c", "d", "a"], a.rotate!
|
||||
assert_equal ["b", "c", "d", "a"], a
|
||||
assert_equal ["d", "a", "b", "c"], a.rotate!(2)
|
||||
assert_equal ["a", "b", "c", "d"], a.rotate!(-3)
|
||||
assert_equal ["c", "d", "a", "b"], a.rotate(10)
|
||||
assert_equal [], [].rotate!
|
||||
end
|
||||
|
||||
assert("Array#delete_if") do
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_equal [1, 2, 3, 4, 5], a.delete_if { false }
|
||||
assert_equal [1, 2, 3, 4, 5], a
|
||||
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_equal [], a.delete_if { true }
|
||||
assert_equal [], a
|
||||
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_equal [1, 2, 3], a.delete_if { |i| i > 3 }
|
||||
assert_equal [1, 2, 3], a
|
||||
end
|
||||
|
||||
assert("Array#reject!") do
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_nil a.reject! { false }
|
||||
assert_equal [1, 2, 3, 4, 5], a
|
||||
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_equal [], a.reject! { true }
|
||||
assert_equal [], a
|
||||
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_equal [1, 2, 3], a.reject! { |val| val > 3 }
|
||||
assert_equal [1, 2, 3], a
|
||||
end
|
||||
|
||||
assert("Array#insert") do
|
||||
a = ["a", "b", "c", "d"]
|
||||
assert_equal ["a", "b", 99, "c", "d"], a.insert(2, 99)
|
||||
assert_equal ["a", "b", 99, "c", 1, 2, 3, "d"], a.insert(-2, 1, 2, 3)
|
||||
|
||||
b = ["a", "b", "c", "d"]
|
||||
assert_equal ["a", "b", "c", "d", nil, nil, 99], b.insert(6, 99)
|
||||
end
|
||||
|
||||
assert("Array#bsearch") do
|
||||
# Find minimum mode
|
||||
a = [0, 2, 4]
|
||||
assert_equal 0, a.bsearch{ |x| x >= -1 }
|
||||
assert_equal 0, a.bsearch{ |x| x >= 0 }
|
||||
assert_equal 2, a.bsearch{ |x| x >= 1 }
|
||||
assert_equal 2, a.bsearch{ |x| x >= 2 }
|
||||
assert_equal 4, a.bsearch{ |x| x >= 3 }
|
||||
assert_equal 4, a.bsearch{ |x| x >= 4 }
|
||||
assert_nil a.bsearch{ |x| x >= 5 }
|
||||
|
||||
# Find any mode
|
||||
a = [0, 4, 8]
|
||||
def between(lo, x, hi)
|
||||
if x < lo
|
||||
1
|
||||
elsif x > hi
|
||||
-1
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
assert_nil a.bsearch{ |x| between(-3, x, -1) }
|
||||
assert_equal 0, a.bsearch{ |x| between(-1, x, 1) }
|
||||
assert_nil a.bsearch{ |x| between( 1, x, 3) }
|
||||
assert_equal 4, a.bsearch{ |x| between( 3, x, 5) }
|
||||
assert_nil a.bsearch{ |x| between( 5, x, 7) }
|
||||
assert_equal 8, a.bsearch{ |x| between( 7, x, 9) }
|
||||
assert_nil a.bsearch{ |x| between( 9, x, 11) }
|
||||
|
||||
assert_equal 0, a.bsearch{ |x| between( 0, x, 3) }
|
||||
assert_equal 4, a.bsearch{ |x| between( 0, x, 4) }
|
||||
assert_equal 4, a.bsearch{ |x| between( 4, x, 8) }
|
||||
assert_equal 8, a.bsearch{ |x| between( 5, x, 8) }
|
||||
|
||||
# Invalid block result
|
||||
assert_raise TypeError, 'invalid block result (must be numeric, true, false or nil)' do
|
||||
a.bsearch{ 'I like to watch the world burn' }
|
||||
end
|
||||
end
|
||||
|
||||
# tested through Array#bsearch
|
||||
#assert("Array#bsearch_index") do
|
||||
#end
|
||||
|
||||
assert("Array#keep_if") do
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_equal [1, 2, 3, 4, 5], a.keep_if { true }
|
||||
assert_equal [1, 2, 3, 4, 5], a
|
||||
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_equal [], a.keep_if { false }
|
||||
assert_equal [], a
|
||||
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_equal [4, 5], a.keep_if { |val| val > 3 }
|
||||
assert_equal [4, 5], a
|
||||
end
|
||||
|
||||
assert("Array#select!") do
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_nil a.select! { true }
|
||||
assert_equal [1, 2, 3, 4, 5], a
|
||||
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_equal [], a.select! { false }
|
||||
assert_equal [], a
|
||||
|
||||
a = [1, 2, 3, 4, 5]
|
||||
assert_equal [4, 5], a.select! { |val| val > 3 }
|
||||
assert_equal [4, 5], a
|
||||
end
|
||||
|
||||
assert('Array#values_at') do
|
||||
a = %w{red green purple white none}
|
||||
|
||||
assert_equal %w{red purple none}, a.values_at(0, 2, 4)
|
||||
assert_equal ['green', 'white', nil, nil], a.values_at(1, 3, 5, 7)
|
||||
assert_equal ['none', 'white', 'white', nil], a.values_at(-1, -2, -2, -7)
|
||||
assert_equal ['none', nil, nil, 'red', 'green', 'purple'], a.values_at(4..6, 0...3)
|
||||
assert_raise(TypeError) { a.values_at 'tt' }
|
||||
end
|
||||
|
||||
assert('Array#to_h') do
|
||||
assert_equal({}, [].to_h)
|
||||
assert_equal({a: 1, b:2}, [[:a, 1], [:b, 2]].to_h)
|
||||
|
||||
assert_raise(TypeError) { [1].to_h }
|
||||
assert_raise(ArgumentError) { [[1]].to_h }
|
||||
end
|
||||
|
||||
assert("Array#index (block)") do
|
||||
assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
|
||||
assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
|
||||
end
|
||||
|
||||
assert("Array#dig") do
|
||||
h = [[[1]], 0]
|
||||
assert_equal(1, h.dig(0, 0, 0))
|
||||
assert_nil(h.dig(2, 0))
|
||||
assert_raise(TypeError) {h.dig(:a)}
|
||||
end
|
||||
|
||||
assert("Array#slice!") do
|
||||
a = [1, 2, 3]
|
||||
b = a.slice!(0)
|
||||
c = [1, 2, 3, 4, 5]
|
||||
d = c.slice!(0, 2)
|
||||
e = [1, 2, 3, 4, 5]
|
||||
f = e.slice!(1..3)
|
||||
g = [1, 2, 3]
|
||||
h = g.slice!(-1)
|
||||
i = [1, 2, 3]
|
||||
j = i.slice!(0, -1)
|
||||
|
||||
assert_equal(a, [2, 3])
|
||||
assert_equal(b, 1)
|
||||
assert_equal(c, [3, 4, 5])
|
||||
assert_equal(d, [1, 2])
|
||||
assert_equal(e, [1, 5])
|
||||
assert_equal(f, [2, 3, 4])
|
||||
assert_equal(g, [1, 2])
|
||||
assert_equal(h, 3)
|
||||
assert_equal(i, [1, 2, 3])
|
||||
assert_equal(j, nil)
|
||||
end
|
||||
|
||||
assert("Array#permutation") do
|
||||
a = [1, 2, 3]
|
||||
assert_permutation([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a)
|
||||
assert_permutation([[1],[2],[3]], a, 1)
|
||||
assert_permutation([[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]], a, 2)
|
||||
assert_permutation([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a, 3)
|
||||
assert_permutation([[]], a, 0)
|
||||
assert_permutation([], a, 4)
|
||||
assert_permutation([], a, -1)
|
||||
end
|
||||
|
||||
assert("Array#combination") do
|
||||
a = [1, 2, 3, 4]
|
||||
assert_combination([[1],[2],[3],[4]], a, 1)
|
||||
assert_combination([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], a, 2)
|
||||
assert_combination([[1,2,3],[1,2,4],[1,3,4],[2,3,4]], a, 3)
|
||||
assert_combination([[1,2,3,4]], a, 4)
|
||||
assert_combination([[]], a, 0)
|
||||
assert_combination([], a, 5)
|
||||
assert_combination([], a, -1)
|
||||
end
|
||||
|
||||
assert('Array#transpose') do
|
||||
assert_equal([].transpose, [])
|
||||
assert_equal([[]].transpose, [])
|
||||
assert_equal([[1]].transpose, [[1]])
|
||||
assert_equal([[1,2,3]].transpose, [[1], [2], [3]])
|
||||
assert_equal([[1], [2], [3]].transpose, [[1,2,3]])
|
||||
assert_equal([[1,2], [3,4], [5,6]].transpose, [[1,3,5], [2,4,6]])
|
||||
assert_raise(TypeError) { [1].transpose }
|
||||
assert_raise(IndexError) { [[1], [2,3,4]].transpose }
|
||||
end
|
||||
Reference in New Issue
Block a user