summaryrefslogtreecommitdiffhomepage
path: root/mrbgems/mruby-array-ext/mrblib/array.rb
diff options
context:
space:
mode:
Diffstat (limited to 'mrbgems/mruby-array-ext/mrblib/array.rb')
-rw-r--r--mrbgems/mruby-array-ext/mrblib/array.rb591
1 files changed, 403 insertions, 188 deletions
diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb
index fd80fa0bb..7520b932f 100644
--- a/mrbgems/mruby-array-ext/mrblib/array.rb
+++ b/mrbgems/mruby-array-ext/mrblib/array.rb
@@ -16,23 +16,19 @@ class Array
# c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
#
def uniq!(&block)
- ary = self.dup
- result = []
+ hash = {}
if block
- hash = {}
- while ary.size > 0
- val = ary.shift
+ self.each do |val|
key = block.call(val)
- hash[key] = val unless hash.has_key?(key)
- end
- hash.each_value do |value|
- result << value
+ hash[key] = val unless hash.key?(key)
end
+ result = hash.values
else
- while ary.size > 0
- result << ary.shift
- ary.delete(result.last)
+ hash = {}
+ self.each do |val|
+ hash[val] = val
end
+ result = hash.keys
end
if result.size == self.size
nil
@@ -55,12 +51,8 @@ class Array
# b.uniq { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
#
def uniq(&block)
- ary = self.dup
- if block
- ary.uniq!(&block)
- else
- ary.uniq!
- end
+ ary = self[0..-1]
+ ary.uniq!(&block)
ary
end
@@ -80,13 +72,40 @@ class Array
hash = {}
array = []
- elem.each { |x| hash[x] = true }
- self.each { |x| array << x unless hash[x] }
+ 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
@@ -104,6 +123,25 @@ class Array
##
# 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
@@ -112,22 +150,90 @@ class Array
# [ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ]
#
def &(elem)
- raise TypeError, "can't convert #{elem.class} into Array" unless elem.class == Array
+ raise TypeError, "cannot convert #{elem.class} into Array" unless elem.class == Array
hash = {}
array = []
- elem.each{|v| hash[v] = true }
- self.each do |v|
+ 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.intersect?(other_ary) -> true or false
+ #
+ # Returns +true+ if the array and +other_ary+ have at least one element in
+ # common, otherwise returns +false+.
+ #
+ # a = [ 1, 2, 3 ]
+ # b = [ 3, 4, 5 ]
+ # c = [ 5, 6, 7 ]
+ # a.intersect?(b) #=> true
+ # a.intersect?(c) #=> false
+ def intersect?(ary)
+ raise TypeError, "cannot convert #{ary.class} into Array" unless ary.class == Array
+
+ hash = {}
+ if self.length > ary.length
+ shorter = ary
+ longer = self
+ else
+ shorter = self
+ longer = ary
+ end
+ idx = 0
+ len = shorter.size
+ while idx < len
+ hash[shorter[idx]] = true
+ idx += 1
+ end
+ idx = 0
+ len = size
+ while idx < len
+ v = longer[idx]
+ if hash[v]
+ return true
+ end
+ idx += 1
+ end
+ false
+ end
+
+ ##
+ # call-seq:
# ary.flatten -> new_ary
# ary.flatten(level) -> new_ary
#
@@ -144,15 +250,9 @@ class Array
# a.flatten(1) #=> [1, 2, 3, [4, 5]]
#
def flatten(depth=nil)
- ar = []
- self.each do |e|
- if e.is_a?(Array) && (depth.nil? || depth > 0)
- ar += e.flatten(depth.nil? ? nil : depth - 1)
- else
- ar << e
- end
- end
- ar
+ res = Array.new(self)
+ res.flatten! depth
+ res
end
##
@@ -175,13 +275,17 @@ class Array
def flatten!(depth=nil)
modified = false
ar = []
- self.each do |e|
+ 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)
@@ -190,44 +294,9 @@ class Array
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_given?
+ return to_enum :reverse_each unless block
i = self.size - 1
while i>=0
@@ -237,7 +306,6 @@ class Array
self
end
- NONE=Object.new
##
# call-seq:
# ary.fetch(index) -> obj
@@ -250,8 +318,9 @@ class Array
# +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.
+ # invalid +index+ is referenced.
+ #
+ # Negative values of +index+ count from the end of the array.
#
# a = [ 11, 22, 33, 44 ]
# a.fetch(1) #=> 22
@@ -261,8 +330,8 @@ class Array
# #=> "100 is out of bounds"
#
- def fetch(n=nil, ifnone=NONE, &block)
- warn "block supersedes default value argument" if n != nil && ifnone != NONE && block
+ def fetch(n, ifnone=NONE, &block)
+ #warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block
idx = n
if idx < 0
@@ -312,51 +381,50 @@ class Array
#
def fill(arg0=nil, arg1=nil, arg2=nil, &block)
- if 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
+ if arg0.nil? && arg1.nil? && arg2.nil?
# ary.fill { |index| block } -> ary
beg = 0
len = self.size
- elsif arg0 != nil && arg0.kind_of?(Range)
+ 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
+ elsif !arg0.nil?
# ary.fill(start [, length] ) { |index| block } -> ary
beg = arg0
beg += self.size if beg < 0
- if arg1 == nil
+ if arg1.nil?
len = self.size
else
len = arg0 + arg1
end
end
else
- if arg0 != nil && arg1 == nil && arg2 == nil
+ 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)
+ 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
+ elsif !arg0.nil? && !arg1.nil?
# ary.fill(obj, start [, length]) -> ary
beg = arg1
beg += self.size if beg < 0
- if arg2 == nil
+ if arg2.nil?
len = self.size
else
len = beg + arg2
@@ -381,57 +449,6 @@ class Array
##
# 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
#
@@ -448,7 +465,7 @@ class Array
# scores.delete_if {|score| score < 80 } #=> [97]
def delete_if(&block)
- return to_enum :delete_if unless block_given?
+ return to_enum :delete_if unless block
idx = 0
while idx < self.size do
@@ -477,7 +494,7 @@ class Array
# If no block is given, an Enumerator is returned instead.
def reject!(&block)
- return to_enum :reject! unless block_given?
+ return to_enum :reject! unless block
len = self.size
idx = 0
@@ -567,64 +584,60 @@ class Array
# undefined which value is actually picked up at each iteration.
def bsearch(&block)
- return to_enum :bsearch unless block_given?
+ return to_enum :bsearch unless block
- low = 0
- high = self.size
- satisfied = false
- while low < high
- mid = low + ((high - low) / 2).truncate
- val = self[mid]
- v = block.call(val)
- if v.is_a?(Integer)
- return val if v == 0
- smaller = v < 0
- elsif v == true
- satisfied = true
- smaller = true
- elsif v == false || v == nil
- smaller = false
- end
- if smaller
- high = mid
- else
- low = mid + 1
- end
+ if idx = bsearch_index(&block)
+ self[idx]
+ else
+ nil
end
- return nil if low == self.size
- return nil unless satisfied
- self[low]
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.
+ # ary.bsearch_index {|x| block } -> int or nil
#
- # See also Array#reject!
- #
- # If no block is given, an Enumerator is returned instead.
+ # 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.
#
- # scores = [ 97, 42, 75 ]
- # scores.delete_if {|score| score < 80 } #=> [97]
+ # 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 delete_if(&block)
- return to_enum :delete_if unless block_given?
+ def bsearch_index(&block)
+ return to_enum :bsearch_index unless block
- idx = 0
- while idx < self.size do
- if block.call(self[idx])
- self.delete_at(idx)
+ 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
- idx += 1
+ 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
- self
+
+ satisfied ? low : nil
end
##
@@ -643,10 +656,9 @@ class Array
# a.keep_if { |val| val > 3 } #=> [4, 5]
def keep_if(&block)
- return to_enum :keep_if unless block_given?
+ return to_enum :keep_if unless block
idx = 0
- len = self.size
while idx < self.size do
if block.call(self[idx])
idx += 1
@@ -672,13 +684,216 @@ class Array
# If no block is given, an Enumerator is returned instead.
def select!(&block)
- return to_enum :select! unless block_given?
+ return to_enum :select! unless block
result = []
- self.each do |x|
- result << x if block.call(x)
+ idx = 0
+ len = size
+ while idx < len
+ elem = self[idx]
+ result << elem if block.call(elem)
+ idx += 1
end
- return nil if self.size == result.size
+ 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>array</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