Class: Array

Inherits:
Object show all
Defined in:
lib/quality_extensions/array/sum.rb,
lib/quality_extensions/array/mean.rb,
lib/quality_extensions/range_list.rb,
lib/quality_extensions/array/average.rb,
lib/quality_extensions/array/justify.rb,
lib/quality_extensions/array/classify.rb,
lib/quality_extensions/array/group_by.rb,
lib/quality_extensions/array/sequence.rb,
lib/quality_extensions/helpers/numbers.rb,
lib/quality_extensions/array/shell_escape.rb,
lib/quality_extensions/array/delete_if_bang.rb,
lib/quality_extensions/array/to_a_recursive.rb,
lib/quality_extensions/array/to_query_string.rb

Overview

Author

Tyler Rick

Copyright

Copyright © 2007 QualitySmith, Inc.

License

Ruby License

Submit to Facets?

Yes.

++

Direct Known Subclasses

Dict, Histogram, RangeList, Table

Instance Method Summary collapse

Instance Method Details

#after(value) ⇒ Object

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.

Example:
     sequence = ['a', 'b', 'c']
     sequence.after('a')           => 'b'
     sequence.after('b')           => 'c'
     sequence.after('c')           => 'a'
     sequence.after('d')           => nil


34
35
36
37
# File 'lib/quality_extensions/array/sequence.rb', line 34

def after(value)
  return nil unless include? value
  self[(index(value).to_i + 1) % length]
end

#averageObject

Calculates the arithmetic average (mean) of the elements in the array as a Float.

irb -> [1, 3, 3].average
    => 2.33333333333333


14
15
16
17
18
19
20
21
# File 'lib/quality_extensions/array/average.rb', line 14

def average
  if self.size == 0
    raise ZeroDivisionError
  end
  self.inject(0.0) do |sum, item|
    sum + item.to_f
  end / self.size
end

#before(value) ⇒ Object

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.

Example:
     sequence = ['a', 'b', 'c']
     sequence.before('a')           => 'c'
     sequence.before('b')           => 'a'
     sequence.before('c')           => 'b'
     sequence.before('d')           => nil


19
20
21
22
# File 'lib/quality_extensions/array/sequence.rb', line 19

def before(value)
  return nil unless include? value
  self[(index(value).to_i - 1) % length]
end

#classify(&block) ⇒ Object

Classifies the array by the return value of the given block and returns a hash of => array of elements pairs.

The block is called once for each element of the array, passing the element as parameter.

Breaks an array into a hash of smaller arrays, making a new group for each unique value returned by the block. Each unique value becomes a key in the hash.

Example:
   [
     ['a', 1],
     ['a', 2],
     ['b', 3],
     ['b', 4],
   ].classify {|o| o[0]}
 =>  
   {
     "a" => [['a', 1], ['a', 2]], 
     "b" => [['b', 3], ['b', 4]]
   }


37
38
39
40
41
42
43
44
# File 'lib/quality_extensions/array/classify.rb', line 37

def classify(&block)
  hash = {}
  each do |element|
    classification = yield(element)
    (hash[classification] ||= []) << element
  end
  hash
end

#delete_if!(&block) ⇒ Object

Like partition, in that it creates two arrays, the first containing the elements of enum for which the block evaluates to false, the second containing the rest, only instead of returning both arrays, it changes self for the first array (those for which the block evaluates to false) and thus only needs to return the second array (those for which the block evaluates to true)

(1..6).partition {|i| i % 3 == 0} # => [[3, 6], [1, 2, 4, 5]]

a = (1..6).to_a
a.delete_if! {|i| i % 3 == 0}     # => [3, 6]
a                                 # => [1, 2, 4, 5]

similar to delete_if / reject!, but modifies self in place (removes elements from self) and rather than simply discarding the deleted elements, it returns an array containing those elements removed (similar to partition)

a more generic version of Facets’ delete_values; that can only be used to delete if a value matches exactly; this can use any arbitrary comparison to determine whether or not to delete element

name?:

modify!
delete_if_returning_deleted
shift_unless :)


82
83
84
85
86
87
88
# File 'lib/quality_extensions/array/delete_if_bang.rb', line 82

def delete_if!(&block)
  d = []
  #each{ |v| d << delete(v) if yield v; puts "yield #{v} returned #{yield v}"}  # didn't work because the deleting messed up the each and not all elements were visited
  each{ |v| d << v if yield v}
  delete_values(*d)
  d
end

#expand_rangesObject

Converts array to a RangeList and expands all Ranges contained in this array, replacing the range with the list of elements that the range represents (range.to_a) .



127
128
129
# File 'lib/quality_extensions/range_list.rb', line 127

def expand_ranges
  to_range_list.expand_ranges
end

#extract_options!Object



14
15
16
# File 'lib/quality_extensions/helpers/numbers.rb', line 14

def extract_options!
  last.is_a?(::Hash) ? pop : {}
end

#group_by(column_index, *args) ⇒ Object

Breaks an array into a hash of smaller arrays, making a new group for each unique value in the specified column.

Each unique value becomes a key in the hash.

Example:
     [
        ['a', 1],
        ['a', 2],
        ['b', 3],
        ['b', 4],
     ].group_by(0)
 =>  
     "a"=>[[1], [2]], 
     "b"=>[[3], [4]]

Options:
* <tt>delete_key</tt>: deletes the key from the corresponding array if true (default true)

Example:
     [
        ['a', 1],
        ['a', 2],
        ['b', 3],
        ['b', 4],
     ].group_by(0, :delete_key => false)
 =>  
     "a"=>[['a', 1], ['a', 2]], 
     "b"=>[['b', 3], ['b', 4]]

*Notes*:
* <tt>self</tt> must be in the shape of a "table" (that is, a rectangular-shaped, two-dimensional array = an array of arrays,
  each member array being of the same size (the "width" of the table)).
* This is different from the GROUP BY in SQL in that it doesn't apply an aggregate (like sum or average) to each group -- it just returns each group unmodified.


53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/quality_extensions/array/group_by.rb', line 53

def group_by(column_index, *args)
  options = (if args.last.is_a?(Hash) then args.pop else {} end)
  hash = {}
  self.each do |row|
    row_to_keep = row.dup
    row_to_keep.delete_values_at(column_index) unless options[:delete_key] == false

    hash[row[column_index]] ||= []
    hash[row[column_index]] << row_to_keep
  end
  hash
end

#ljust_columns(padstr = ' ') ⇒ Object

In each column of a table (2-dimensional array) of strings, pads the string with padstr (using ljust) so that all strings in that column have the same length.

[['a',   'bb'],
 ['aaa', 'b' ]].ljust_columns

> [[‘a ’, ‘bb’],

['aaa', 'b ']]

The new values in the table will be strings even if they were not originally (1 might turn into ‘1 ’, for example).



25
26
27
28
29
# File 'lib/quality_extensions/array/justify.rb', line 25

def ljust_columns(padstr = ' ')
  new = self.deep_copy
  new.ljust_columns!(padstr)
  new
end

#ljust_columns!(padstr = ' ') ⇒ Object

In-place version of ljust_columns.



46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/quality_extensions/array/justify.rb', line 46

def ljust_columns!(padstr = ' ')
  matrix = Matrix[*self]
  matrix.column_vectors.each do |column| 
    max_length_in_col = column.max_by { |el|
      el.to_s.length 
    }.to_s.length
    column.each_with_index { |el, i| 
      column[i] = el.to_s.ljust(max_length_in_col, padstr)
    }
  end
  self
end

#ljust_rows(padstr = ' ') ⇒ Object

In each row of a table (2-dimensional array) of strings, pads the string with padstr (using ljust) so that all strings in that row have the same length.

[['a'  , 'aa' , 'a'  ],
 ['bbb', 'b'  , 'b'  ]].ljust_rows

> [[‘a ’ , ‘aa’ , ‘a ’ ],

['bbb', 'b  ', 'b  ']]

The new values in the table will be strings even if they were not originally (1 might turn into ‘1 ’, for example).



68
69
70
71
72
# File 'lib/quality_extensions/array/justify.rb', line 68

def ljust_rows(padstr = ' ')
  new = self.deep_copy
  new.ljust_rows!(padstr)
  new
end

#ljust_rows!(padstr = ' ') ⇒ Object

In-place version of ljust_rows.



90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/quality_extensions/array/justify.rb', line 90

def ljust_rows!(padstr = ' ')
  matrix = Matrix[*self]
  matrix.row_vectors.each do |row| 
    max_length_in_row = row.max_by { |el|
      el.to_s.length 
    }.to_s.length
    row.each_with_index { |el, i| 
      row[i] = el.to_s.ljust(max_length_in_row, padstr)
    }
  end
  self
end

#meanObject

Calculates the arithmetic average (mean) of the elements in the array as a Float.

irb -> [1, 3, 3].average
    => 2.33333333333333


14
15
16
17
18
19
20
21
# File 'lib/quality_extensions/array/mean.rb', line 14

def mean
  if self.size == 0
    raise ZeroDivisionError
  end
  self.inject(0.0) do |sum, item|
    sum + item.to_f
  end / self.size
end

#rjust_columns(padstr = ' ') ⇒ Object

In each column of a table (2-dimensional array) of strings, pads the string with padstr (using rjust) so that all strings in that column have the same length.

[[  'a', 'bb'],
 ['aaa',  'b']].rjust_columns

> [[‘ a’, ‘bb’],

['aaa', ' b']]

The new values in the table will be strings even if they were not originally (1 might turn into ‘1 ’, for example).



115
116
117
118
119
# File 'lib/quality_extensions/array/justify.rb', line 115

def rjust_columns(padstr = ' ')
  new = self.deep_copy
  new.rjust_columns!(padstr)
  new
end

#rjust_columns!(padstr = ' ') ⇒ Object

In-place version of rjust_columns.



123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/quality_extensions/array/justify.rb', line 123

def rjust_columns!(padstr = ' ')
  matrix = Matrix[*self]
  matrix.column_vectors.each do |column| 
    max_length_in_col = column.max_by { |el|
      el.to_s.length 
    }.to_s.length
    column.each_with_index { |el, i| 
      column[i] = el.to_s.rjust(max_length_in_col, padstr)
    }
  end
  self
end

#rjust_rows(padstr = ' ') ⇒ Object

In each row of a table (2-dimensional array) of strings, pads the string with padstr (using rjust) so that all strings in that row have the same length.

[[  'a',  'aa',   'a'],
 ['bbb',   'b',   'b']].rjust_rows

> [[ ‘ a’, ‘aa’, ‘ a’],

['bbb', '  b', '  b']]

The new values in the table will be strings even if they were not originally (1 might turn into ‘1 ’, for example).



145
146
147
148
149
# File 'lib/quality_extensions/array/justify.rb', line 145

def rjust_rows(padstr = ' ')
  new = self.deep_copy
  new.rjust_rows!(padstr)
  new
end

#rjust_rows!(padstr = ' ') ⇒ Object

In-place version of rjust_rows.



153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/quality_extensions/array/justify.rb', line 153

def rjust_rows!(padstr = ' ')
  matrix = Matrix[*self]
  matrix.row_vectors.each do |row| 
    max_length_in_row = row.max_by { |el|
      el.to_s.length 
    }.to_s.length
    row.each_with_index { |el, i| 
      row[i] = el.to_s.rjust(max_length_in_row, padstr)
    }
  end
  self
end

#select_if!(&block) ⇒ Object

Like partition, in that it creates two arrays, the first containing the elements of enum for which the block evaluates to false, the second containing the rest, only instead of returning both arrays, it changes self for the first array (those for which the block evaluates to false) and thus only needs to return the second array (those for which the block evaluates to true)

this is analagous to what shift (or pop) does: it shifts (or pops) an element off of a collection and simultaneously returns that element which was shifted (or popped) off

Example:

orig_a = a = (1..6).to_a
b = a.select_if! {|i| i % 3 == 0}   # => [1, 2, 4, 5] 
a                                   # => [3, 6]

This is identical to doing this:

orig_a = a = (1..6).to_a
a, b = a.partition {|i| i % 3 == 0}
a                                   # => [3, 6]
b                                   # => [1, 2, 4, 5]

except that in the case of partition, orig_a and a are now to different objects, whereas with select_if, a remains the same object and is modified in place.

name?:

modify!
select_if_returning_deleted
shift_if :)


39
40
41
42
43
44
# File 'lib/quality_extensions/array/delete_if_bang.rb', line 39

def select_if!(&block)
  d = []
  each{ |v| d << v unless yield v}
  delete_values(*d)
  d
end

#select_if_with_index!(&block) ⇒ Object

Note that self is not modified (the values are not deleted) until it has finished iterating through elements and has given a chance for you to decide the fate of each element. (So if, for instance, you want to refer to the previous value, you can do that with select_if_with_index! by using array, even if array is “scheduled for deletion”, because it will not have been deleted yet.)

Example: args_for_vim = ARGV.delete_if! {|arg, i|

arg =~ /^-c/ || ARGV[i-1] =~ /^-c/ \
  || arg =~ /^\+/

}



56
57
58
59
60
61
# File 'lib/quality_extensions/array/delete_if_bang.rb', line 56

def select_if_with_index!(&block)
  d = []
  each_with_index{ |v,i| d << v unless yield v,i}
  delete_values(*d)
  d
end

#shell_escapeObject



13
14
15
# File 'lib/quality_extensions/array/shell_escape.rb', line 13

def shell_escape
  self.map(&:shell_escape).map(&:to_s)
end

#sumObject



11
12
13
# File 'lib/quality_extensions/array/sum.rb', line 11

def sum
  inject( nil ) { |sum,x| sum ? sum+x : x }
end

#to_a_recursiveObject

A lot like array.flatten, except that it will also “flatten” ranges (by converting range to range.to_a) and any other objects that respond to to_a contained in the array in addition to arrays contained in the array. Compare with Array#expand_ranges



12
13
14
15
16
17
18
19
20
21
# File 'lib/quality_extensions/array/to_a_recursive.rb', line 12

def to_a_recursive
  map do |item|
    # Assume that all arrays contain only elements that do not respond to to_a_recursive or arrays.
    if item.respond_to? :to_a_recursive
      item.to_a_recursive
    else
      item.to_a
    end
  end
end

#to_query_string(key) ⇒ Object

Converts into a string that can be used as the query string of a URL (for example, ?key[]=val1&key[]=val2).

Example:
  [
    'Fred',
    'Sam'
  ].to_query_string('names')
  ==> "names[]=Fred&names[]=Sam"

<tt>key</tt> is the name of the key in params that will receive the array when you load the page. So, for example, if you go to page with this query string (key = "names"): <tt>?names[]=Fred&names[]=Sam</tt>, params will be have a key "names", like so: <tt>{"names"=>["Fred", "Sam"]}</tt>.


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/quality_extensions/array/to_query_string.rb', line 23

def to_query_string(key)
  elements = []
  
  self.each do |value|
    _key = key + '[]'
    if value.is_a? Array
      raise "Array#to_query_string only works on flat (1-dimensional) arrays."
    elsif value.respond_to? :to_query_string
    	value = value.to_query_string(_key)
    else
      value = CGI.escape value.to_s
    end
    elements << _key + '=' + value
  end
  
  elements.join('&')
end

#to_range_listObject



121
122
123
# File 'lib/quality_extensions/range_list.rb', line 121

def to_range_list
  RangeList.new(self)
end