class Array
Public Instance Methods
Array#** is an alias for #product.
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/array/op_pow'
Returns the value after the given value. The value before the last is the first. Returns nil if the given value is not in the array.
Examples
sequence = ['a', 'b', 'c'] sequence.after('a') #=> 'b' sequence.after('b') #=> 'c' sequence.after('c') #=> 'a' sequence.after('d') #=> nil
CREDIT: Tyler Rick
# File lib/facets/array/before.rb, line 33 def after(value) return nil unless include? value self[(index(value).to_i + 1) % length] end
Returns the value previous to the given value. The value previous to the first is the last. Returns nil if the given value is not in the array.
Examples
sequence = ['a', 'b', 'c'] sequence.before('a') #=> 'c' sequence.before('b') #=> 'a' sequence.before('c') #=> 'b' sequence.before('d') #=> nil
CREDIT: Tyler Rick
# File lib/facets/array/before.rb, line 16 def before(value) return nil unless include? value self[(index(value).to_i - 1) % length] end
Simplify an array by flattening it then compacting it.
[1,[2,nil,[3]],nil,4].collapse #=> [1,2,3,4]
# File lib/facets/array/collapse.rb, line 7 def collapse flatten.compact end
Yields the block to each unique combination of n elements.
a = %w|a b c d| a.combination(3)
produces
[["a", "b", "c"], ["a", "b", "d"], ["a", "c", "d"], ["b", "c", "d"]]
CREDIT: Florian Gross
# File lib/facets/array/combination.rb, line 21 def combination(k=2) if block_given? s = to_a n = s.size return unless (1..n) === k idx = (0...k).to_a loop do yield s.values_at(*idx) i = k - 1 i -= 1 while idx[i] == n - k + i break if i < 0 idx[i] += 1 (i + 1 ... k).each {|j| idx[j] = idx[i] + j - i} end else to_enum(:combination, k) end end
Returns all items that are equal in terms of the supplied block. If no block is given objects are considered to be equal if they return the same value for Object#hash and if obj1 == obj2.
[1, 2, 2, 3, 4, 4].commonality #=> { 2 => [2, 2], 4 => [4, 4] } ["foo", "bar", "a"].commonality { |str| str.length } #=> { 3 => ["foo", "bar"] }
This can be useful, for instance, in determining all persons that share their last name with another person …
persons.collisions { |person| person.last_name }
CREDIT: Florian Gross
# File lib/facets/array/commonality.rb, line 19 def commonality(&block) had_no_block = !block block ||= lambda { |item| item } result = Hash.new { |hash, key| hash[key] = Array.new } each do |item| key = block.call(item) result[key] << item end result.reject! do |key, values| values.size <= 1 end # -- return had_no_block ? result.values.flatten : result return result end
This is more advanced form of join. It allows for fine control of separators.
NOTE: The old version used to default its separator to “, ” and default the terminating separator to “ and ”. This is no longer the case. You must specifically provide these parameters.
If no paramters are given, it acts like join but will a space separator.
[1,2,3].conjoin #=> "1 2 3"
Use comma+space and 'and' on tail.
[1,2,3].conjoin(', ', ' and ') #=> "1, 2 and 3"
Use comma+space and 'or' on tail using :last option.
[1,2,3].conjoin(', ', :last => ' or ') #=> "1, 2 or 3"
Use semicolon+space and ampersand on tail using index.
[1,2,3].conjoin('; ', -1 => ' & ') #=> "1; 2 & 3"
Can take a block to determine separator.
[1,2,3,4].conjoin{ |i, a, b| i % 2 == 0 ? '.' : '-' } #=> "1.2-3.4"
This makes very esoteric transformation possible.
[1,1,2,2].conjoin{ |i, a, b| a == b ? '=' : ' != ' } #=> "1=1 != 2=2" [1,2,3,4].conjoin{ |i, x, y| "<#{i} #{x} #{y}>" } #=> "1<0 1 2>2<1 2 3>3<2 3 4>4"
There are also spacing options. Providing the :space option pads the separators.
[1,2,3].conjoin(',', '&', :space=>2) #=> "1 , 2 & 3"
And the :spacer option can set an alternate spacing string.
[1,2,3].conjoin('|', '>', :space=>2, :spacer=>'-') #=> "1--|--2-->--3"
CREDIT: Trans
# File lib/facets/array/conjoin.rb, line 57 def conjoin(*args, &block) return first.to_s if size < 2 options = (Hash===args.last) ? args.pop : {} spacing = options.delete(:space) || 0 spacer = options.delete(:spacer) || " " space = spacer * spacing.to_i sep = [] if block_given? (size - 1).times do |i| sep << space + yield(i, *slice(i,2)) + space end else separator = args.shift || " " options[-1] = args.shift if args.first options[0] = options.delete(:first) if options.key?(:first) options[-1] = options.delete(:last) if options.key?(:last) separator = space + separator + space sep = [separator] * (size - 1) options.each{|i, s| sep[i] = space + s + space} end zip(sep).join end
Inverse of delete_if.
[1,2,3].delete_unless{ |x| x < 2 } #=> [1]
CREDIT: Daniel Schierbeck
# File lib/facets/array/delete_unless.rb, line 10 def delete_unless(&block) delete_if { |element| not block.call(element) } end
Delete multiple values from array.
a = [1,2,3,4] a.delete_values(1,2) #=> [1,2] a #=> [3,4]
CREDIT: Trans
# File lib/facets/array/delete_values.rb, line 11 def delete_values(*values) d = [] values.each{ |v| d << delete(v) } d end
Delete multiple values from array given indexes or index range.
a = [1,2,3,4] a.delete_values_at(1,2) #=> [2,3] a #=> [1,4] a = [1,2,3,4] a.delete_values_at(0..2) #=> [1,2,3] a #=> [4]
NOTE: It would be nice to see delete_at incorporate this funcitonaility.
CREDIT: Trans
# File lib/facets/array/delete_values.rb, line 32 def delete_values_at(*selectors) idx = [] selectors.each{ |i| case i when Range idx.concat( i.to_a ) else idx << i.to_i end } idx.uniq! dvals = values_at(*idx) idx = (0...size).to_a - idx self.replace( values_at(*idx) ) return dvals end
Divide on matching pattern.
['a1','b1','a2','b2'].divide(/^a/) #=> [['a1','b1'],['a2','b2']]
CREDIT: Trans
# File lib/facets/array/divide.rb, line 10 def divide(pattern) memo = [] each do |obj| memo.push [] if pattern === obj memo.last << obj end memo end
Return list of duplictate elements.
@param [Integer] min
The minimum number of duplicatation necessary for inclusion.
@author Rebort Dober (current implementation) @author Thibaut Barrère
# File lib/facets/array/nonuniq.rb, line 38 def duplicates(min=2) h = Hash.new( 0 ) each {|i| h[i] += 1 } h.delete_if{|_,v| v < min}.keys end
Iterate over index and value. The intention of this method is to provide polymorphism with Hash.
# File lib/facets/array/each_pair.rb, line 6 def each_pair #:yield: i = -1 each_value do |x| yield(i+=1, x) end end
Shannon's entropy for an array - returns the average bits per symbol required to encode the array. Lower values mean less “entropy” - i.e. less unique information in the array.
e = %w{ a b c d e e e }.entropy ("%.3f" % e) #=> "2.128"
CREDIT: Derek
# File lib/facets/array/entropy.rb, line 16 def entropy arr = self probHash = arr.probability # -- h is the Shannon entropy of the array h = -1.to_f * probHash.keys.inject(0.to_f) do |sum, i| sum + (probHash[i] * (Math.log(probHash[i])/Math.log(2.to_f))) end h end
Extracts options from a set of arguments. Removes and returns the last element in the array if it's a hash, otherwise returns a blank hash.
def options(*args) args.extract_options! end options(1, 2) # => {} options(1, 2, :a => :b) # => {:a=>:b}
# File lib/facets/array/extract_options.rb, line 23 def extract_options! if Hash === last && last.extractable_options? pop else {} end end
Returns last n elements.
%w{W o r l d}.from(3) #=> %w{l d}
# File lib/facets/array/from.rb, line 7 def from(i) return self if i >= size self[i, size - i] end
Returns the maximum possible Shannon entropy of the array with given size assuming that it is an “order-0” source (each element is selected independently of the next).
CREDIT: Derek
# File lib/facets/array/entropy.rb, line 32 def ideal_entropy arr = self unitProb = 1.0.to_f / arr.size.to_f (-1.to_f * arr.size.to_f * unitProb * Math.log(unitProb)/Math.log(2.to_f)) end
Determines the sorted middle element.
a = %w{a a b b c c c} a.median #=> "b"
When there are an even number of elements, the greater of the two middle elements is given.
a = %w{a a b b c c c d} a.median #=> "c"
An offset can be supplied to get an element relative to the middle.
a = %w{a a b b c c c d} a.median(-1) #=> "b"
The the array is empty, nil
is returned.
@return [Object] sorted middle element
# File lib/facets/array/median.rb, line 23 def median(offset=0) return nil if self.size == 0 tmp = self.sort mid = (tmp.size / 2).to_i + offset tmp[mid] end
In place merge.
a = [1,2] a.merge! [2,3] a #=> [1,2,3]
CREDIT: Trans
# File lib/facets/array/merge.rb, line 11 def merge!( other ) self.replace(self.merge(other)) end
In Statistics mode is the value that occurs most frequently in a given set of data. This method returns an array in case their is a tie.
[:a, :b, :c, :b, :d].mode #=> [:b] [:a, :b, :c, :b, :a].mode #=> [:a, :b]
Returns an Array of most common elements.
@author Robert Klemme
# File lib/facets/array/mode.rb, line 14 def mode max = 0 c = Hash.new 0 each {|x| cc = c[x] += 1; max = cc if cc > max} c.select {|k,v| v == max}.map {|k,v| k} end
Returns a list of non-unique elements
[1,1,2,2,3,4,5].nonuniq #=> [1,2]
@author Martin DeMello
# File lib/facets/array/nonuniq.rb, line 9 def nonuniq h1 = {} h2 = {} each {|i| h2[i] = true if h1[i] h1[i] = true } h2.keys end
Same as `#nonuniq` but acting in place.
# File lib/facets/array/nonuniq.rb, line 20 def nonuniq! h1 = {} h2 = {} each {|i| h2[i] = true if h1[i] h1[i] = true } self.replace(h2.keys) end
Not empty?
[].not_empty? #=> false [1,2].not_empty? #=> true
# File lib/facets/array/not_empty.rb, line 8 def not_empty? !empty? end
# File lib/facets/object/object_state.rb, line 47 def object_state(data=nil) data ? replace(data) : dup end
Returns a list of elements that occur n
times.
[0,1,1,1,3,0,1,2,4].occurent(3) #=> [1]
If n
is a Range then returns elements
that occur a number of time within the range.
[0,1,1,1,3,0,1,2,4].occurent(2..4) #=> [0,1]
@author Robert Dober
# File lib/facets/array/nonuniq.rb, line 57 def occurent(n=2) h = Hash.new( 0 ) each do |i| h[i] += 1 end case n when nil h.delete_if{ |_,v| ! yield(v) }.keys when Range h.delete_if{ |_,v| ! n.include?(v) }.keys else h.delete_if{|_,v| v != n}.keys end end
Returns the only element in the array. Raises an IndexError if the array's size is not 1.
[5].only # => 5 expect IndexError do [1,2,3].only end expect IndexError do [].only end
CREDIT: Gavin Sinclair, Noah Gibbs
# File lib/facets/array/only.rb, line 18 def only unless size == 1 raise IndexError, "Array#only called on non-single-element array" end first end
Pad an array with a given value
up to a given
length
.
[0,1,2].pad(6,"a") #=> [0,1,2,"a","a","a"]
If length
is a negative number padding will be added to the
beginning of the array.
[0,1,2].pad(-6,"a") #=> ["a","a","a",0,1,2]
CREDIT: Richard Laugesen
# File lib/facets/array/pad.rb, line 14 def pad(len, val=nil) return dup if self.size >= len.abs if len < 0 Array.new((len+size).abs,val) + self else self + Array.new(len-size,val) end end
Like pad but changes the array in place.
a = [0,1,2] a.pad!(6,"x") a #=> [0,1,2,"x","x","x"]
CREDIT: Richard Laugesen
# File lib/facets/array/pad.rb, line 31 def pad!(len, val=nil) return self if self.size >= len.abs if len < 0 replace Array.new((len+size).abs,val) + self else concat Array.new(len-size,val) end end
Peek at the top of the stack (the end of the array).
a = [1, 2, 3] a.peek #=> 3 a #=> [1, 2, 3]
Or provide an index to inspect the array from back to front.
# File lib/facets/array/pull.rb, line 14 def peek(i=0) i = -(i + 1) fetch(i) end
Permutation provids the possible orders of an enumerable. Each is indexed by a permutation number. The maximum number of arrangements is the factorial of the size of the array.
[1,2].permutation(2).to_a #=> [[1,2], [2,1]]
CREDIT: Shin-ichiro Hara
# File lib/facets/array/permutation.rb, line 13 def permutation(n=size) if size < n or n < 0 elsif n == 0 yield([]) else self[1..-1].permutation(n - 1) do |x| (0...n).each do |i| yield(x[0...i] + [first] + x[i..-1]) end end self[1..-1].permutation(n) do |x| yield(x) end end end
Put an object on the bottom of the stack (front of the array).
a = [2, 3] a.poke(1) a #=> [1, 2, 3]
Or supply an index and poke works like insert.
# File lib/facets/array/pull.rb, line 26 def poke(x, i=0) insert(i,x) end
Generates a hash mapping each unique element in the array to the relative frequency, i.e. the probablity, of it appearence.
[:a, :b, :c, :c].probability #=> {:a=> 0.25, :b=>0.25, :c=>0.5}
CREDIT: Brian Schröder
# File lib/facets/array/probability.rb, line 10 def probability probs = Hash.new(0.0) size = 0.0 each do |e| probs[e] += 1.0 size += 1.0 end probs.keys.each{ |e| probs[e] /= size } probs end
Provides the cartesian product of two or more arrays.
a = [1,2].product([4,5]) a #=> [[1, 4],[1, 5],[2, 4],[2, 5]]
CREDIT: Thomas Hafner
# File lib/facets/array/product.rb, line 12 def product(*enums) enums.unshift self result = [[]] while [] != enums t, result = result, [] b, *enums = enums t.each do |a| b.each do |n| result << a + [n] end end end result end
Apply a block to array, and recursively apply that block to each sub-array
or types
.
arr = ["a", ["b", "c", nil], nil] arr.recurse{ |a| a.compact! } #=> ["a", ["b", "c"]]
# File lib/facets/array/recurse.rb, line 10 def recurse(*types, &block) types = [self.class] if types.empty? a = inject([]) do |array, value| case value when *types array << value.recurse(*types, &block) else array << value end array end yield a end
In place form of recurse.
# File lib/facets/array/recurse.rb, line 25 def recurse!(&block) replace(recurse(&block)) end
Apply a method to array, and recursively apply that method to each
sub-array or types
.
arr = ["a", ["b", "c"]] arr.recursively.map{ |v| v.to_sym } #=> [:a, [:b, :c]]
By default the sub-types are passed thru uneffected. Passing a block to recursively changes this.
arr = ["a", ["b", "c"]] arr.recursively{ |a| a.reverse }.map{ |v| v.to_sym } #=> [:a, [:c, :b]]
TODO: Return Enumerator if no
yld
block is given ?
# File lib/facets/array/recursively.rb, line 21 def recursively(*types, &block) Recursor.new(self, *types, &block) end
Rotates an array's elements from back to front n times.
[1,2,3].rotate #=> [2,3,1] [2,3,1].rotate #=> [3,1,2] [3,1,2].rotate #=> [1,2,3] [1,2,3].rotate(3) #=> [1,2,3]
A negative parameter reverses the order from front to back.
[1,2,3].rotate(-1) #=> [3,1,2]
CREDIT: Florian Gross, Thomas Sawyer
# File lib/facets/array/rotate.rb, line 19 def rotate(n=1) self.dup.rotate!(n) end
Same as rotate, but acts in place.
a = [1,2,3] a.rotate! a #=> [2,3,1]
CREDIT: Florian Gross, Thomas Sawyer
# File lib/facets/array/rotate.rb, line 35 def rotate!(n=1) n = n.to_int return self if (n == 0 or self.empty?) if n < 0 n.abs.times{ self.unshift( self.pop ) } else n.abs.times{ self.push( self.shift ) } end self end
As with select but modifies the Array in place.
a = [1,2,3,4,5,6,7,8,9,10] a.select!{ |e| e % 2 == 0 } a #=> [2,4,6,8,10]
CREDIT: Gavin Sinclair
# File lib/facets/array/select.rb, line 13 def select! # :yield: reject!{ |e| not yield(e) } end
Splice acts a combination of slice! and store. If two arguments are given it calls store. If a single argument is given it calls slice!.
a = [1,2,3] a.splice(1) #=> 2 a #=> [1,3] a = [1,2,3] a.splice(1,4) #=> 4 a #=>[1,4,3]
CREDIT: Trans
# File lib/facets/array/splice.rb, line 19 def splice(*args) if args.size == 1 slice!(*args) else store(*args) end end
Split on matching pattern. Unlike divide this does not include matching elements.
['a1','a2','b1','a3','b2','a4'].split(/^b/) #=> [['a1','a2'],['a3'],['a4']]
CREDIT: Trans
# File lib/facets/array/split.rb, line 10 def split(pattern) memo = [] sect = [] each do |obj| if pattern === obj memo << sect sect = [] else sect << obj end end memo << sect memo.pop while memo.last == [] memo end
Fetch values from a start index thru an end index.
[1,2,3,4,5].thru(0,2) #=> [1,2,3] [1,2,3,4,5].thru(2,4) #=> [3,4,5] [1,2,3,4,5].thru(2) #=> [1,2,3] [1,2,3,4,5].thru(4) #=> [1,2,3,4,5]
# File lib/facets/array/from.rb, line 20 def thru(from, to=nil) from, to = 0, from unless to to = size - 1 if to >= size a = [] i = from while i <= to a << self[i] i += 1 end a end
Boolean conversion for not empty?
# File lib/facets/boolean.rb, line 110 def to_b ! self.empty? end
Converts an array into a hash. Converting an array into a hash is not a one-to-one conversion, for this reason to_h examines at the array being converted and then dispatches the conversion to the most sutiable specialized function. There are three possiblities for this.
If the array is a collection of perfect pairs, like that which Hash#to_a generates, then conversion is handled by to_h_flat.
a = [ [:a,1], [:b,2] ] a.to_h #=> { :a=>1, :b=>2 }
If the array contains only arrays, but are not perfect pairs, then to_h_multi is called.
a = [ [:a,1,2], [:b,2], [:c], [:d] ] a.to_h #=> { :a=>[1,2], :b=>[2], :c=>[], :d=>[] }
If the array contians objects other then arrays then the to_h_splat method is called.
a = [ [:a,1,2], 2, :b, [:c,3], 9 ] a.to_h #=> { [:a,1,2]=>2, :b=>[:c,3], 9=>nil }
Finally, a particular dispatch can be forced by specifying the
mode
of conversion, eg. :multi
,
:splat
, :flat
, :assoc
, etc.
Setting mode
to true
is the same as setting it
:multi
. This has been left in for backward compatability.
NOTE: The use of a values
parameter has been deprecated
because that functionality is as simple as …
array1.zip(array2).to_h
CREDIT: Robert Klemme, Trans
# File lib/facets/to_hash.rb, line 51 def to_h(mode=nil) case mode when :splat return to_h_splat when :flat return to_h_flat when :multi, true return to_h_multi when :assoc return to_h_assoc else return to_h_auto end end
When a mixed or multi-element accociative array is used, the result is as follows:
a = [ [:a,1,2], [:b,2], [:c], :d ] a.to_h #=> { :a=>[1,2], :b=>[2], :c=>[], :d=>[] }
If the first entry of any subelements are the same, then the value will be set to the last occuring value.
a = [ :x, [:x], [:x,1,2], [:x,3], [:x,4] ] a.to_h_assoc #=> { :x=>[4] }
# File lib/facets/to_hash.rb, line 157 def to_h_assoc h = {} each do |k,*v| h[k] = v end h end
Converts an array into a hash. Converting an array into a hash is not a one-to-one conversion, for this reason to_h examines at the array being converted and then dispatches the conversion to the most sutiable specialized function. There are three possiblities for this.
If the array is a collection of perfect pairs, like that which Hash#to_a generates, then conversion is handled by to_h_flat.
a = [ [:a,1], [:b,2] ] a.to_h_auto #=> { :a=>1, :b=>2 }
If the array contains only arrays, but are not perfect pairs, then to_h_multi is called.
a = [ [:a,1,2], [:b,2], [:c], [:d] ] a.to_h_auto #=> { :a=>[1,2], :b=>[2], :c=>[], :d=>[] }
If the array contians objects other then arrays then the to_h_splat method is called.
a = [ [:a,1,2], 2, :b, [:c,3], 9 ] a.to_h_auto #=> { [:a,1,2]=>2, :b=>[:c,3], 9=>nil }
# File lib/facets/to_hash.rb, line 91 def to_h_auto pairs = true mixed = false each do |e| case e when Array pairs = false if e.size > 2 else mixed = true end end if mixed to_h_splat elsif pairs to_h_flat else to_h_multi end end
This is equivalent to Hash, but it will pad
the array with a nil
object if there are not an even number of
elements.
a = [:a,1,[:b,2,:c]] a.to_h_flat #=> { :a=>1, :b=>2, :c=>nil }
# File lib/facets/to_hash.rb, line 133 def to_h_flat a = flatten a << nil if a.size % 2 == 1 Hash[*a] end
When a mixed or multi-element accociative array is used, the result is as follows:
a = [ [:a,1,2], [:b,2], [:c], :d ] a.to_h #=> { :a=>[1,2], :b=>[2], :c=>[], :d=>[] }
If the first entry of the subelements is the same, then the values will be merged using concat.
a = [ [:a,1,2], [:a,3], [:a,4], [:a], :a ] a.to_h_multi #=> { :a=>[1,2,3,4] }
# File lib/facets/to_hash.rb, line 177 def to_h_multi h = {} each do |k,*v| h[k] ||= [] h[k].concat(v) end h end
This is equivalent to Hash, but it will pad the array
with a nil
object if there are not an even number of elements.
a = [:a,1,:b,2,:c] a.to_h_splat #=> { :a=>1, :b=>2, :c=>nil }
# File lib/facets/to_hash.rb, line 120 def to_h_splat a = dup a << nil if a.size % 2 == 1 Hash[*a] end
Returns a new array created by traversing the array and its sub-arrays, executing the given block on the elements.
h = ["A", "B", ["X", "Y"]] g = h.traverse{ |e| e.downcase } g #=> ["a", "b", ["x", "y"]]
This is the same as recursive.map
and will likely be
deprecated in the future because of it.
CREDIT: Trans
# File lib/facets/array/traverse.rb, line 16 def traverse(&block) if block_given? map do |e| if e.respond_to?(:to_ary) e.to_ary.traverse(&block) else block.call(e) end end else to_enum(:traverse) end end
Like recursive_map, but will change the array in place.
h = ["A", "B", ["X", "Y"]] h.traverse!{ |e| e.downcase } h #=> ["a", "b", ["x", "y"]]
CREDIT: Trans
# File lib/facets/array/traverse.rb, line 39 def traverse!(&block) replace(traverse(&block)) end
Like uniq, but determines uniqueness based on a given block.
a = (-5..5).to_a a.uniq_by!{ |i| i*i } a #=> [-5, -4, -3, -2, -1, 0]
As can be seen in the example, order is significant.
# File lib/facets/array/uniq_by.rb, line 10 def uniq_by! #:yield: h = {} replace( inject([]){|a,x| h[yield(x)] ||= a << x} ) end