functional-ruby 0.7.4 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +46 -12
- data/lib/functional.rb +19 -0
- data/lib/functional/catalog.rb +487 -0
- data/lib/functional/collection.rb +403 -0
- data/lib/functional/inflect.rb +127 -0
- data/lib/functional/search.rb +132 -0
- data/lib/functional/sort.rb +41 -0
- data/lib/functional/utilities.rb +0 -3
- data/lib/functional/version.rb +1 -1
- data/md/catalog.md +32 -0
- data/md/collection.md +32 -0
- data/md/inflect.md +32 -0
- data/md/pattern_matching.md +2 -2
- data/md/platform.md +32 -0
- data/md/search.md +32 -0
- data/md/sort.md +32 -0
- data/md/utilities.md +2 -2
- data/spec/functional/catalog_spec.rb +1206 -0
- data/spec/functional/collection_spec.rb +752 -0
- data/spec/functional/inflect_spec.rb +85 -0
- data/spec/functional/pattern_matching_spec.rb +0 -2
- data/spec/functional/search_spec.rb +187 -0
- data/spec/functional/sort_spec.rb +61 -0
- data/spec/spec_helper.rb +9 -0
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97e8113fb43fd8fda7f581f0e3bc342e66b42cdb
|
4
|
+
data.tar.gz: c64e9d6a0788814d50bdc87b00ca7d00caa887a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac1e57dac94eedaf8a94a45715ecc5757238afa8a7fe4dc24e57b3255d1ce7f1e64a3b2d562c099d0b77ca1b815778905ad561c09e46c337c1b43c0e4c4188f2
|
7
|
+
data.tar.gz: 4db984862cf3b281ed4251e4fd1d6f8702e015f47a348d091080f84df0479cdc3fa77ecc2eafb0c64801c7e7d8a58ca8d7a3f1dd8ef7e1006359f02e498f94d6
|
data/README.md
CHANGED
@@ -16,21 +16,50 @@ The project is hosted on the following sites:
|
|
16
16
|
|
17
17
|
## Introduction
|
18
18
|
|
19
|
-
|
19
|
+
Two things I love are [Ruby](http://www.ruby-lang.org/en/) and
|
20
20
|
[functional](https://en.wikipedia.org/wiki/Functional_programming)
|
21
|
-
[programming](http://c2.com/cgi/wiki?FunctionalProgramming)
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
[programming](http://c2.com/cgi/wiki?FunctionalProgramming).
|
22
|
+
Sadly, the former is generally not associated with the latter. Unfortunately,
|
23
|
+
too many people are blinded by their belief that Ruby is an object-oriented
|
24
|
+
language. I reject this assertion. Ruby is certainly object-based, since
|
25
25
|
everything is an object, but entire large-scale programs can be built without ever
|
26
|
-
defining a single class. Ruby
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
defining a single class. But Ruby's features that support functional programming
|
27
|
+
don't stop there.
|
28
|
+
|
29
|
+
Ask ten different programmers to define the term "functional programming" and
|
30
|
+
you will likely get ten different answers. One characteristic that will certainly
|
31
|
+
be on all their lists is support for
|
32
|
+
[higher](http://en.wikipedia.org/wiki/Higher-order_function)
|
33
|
+
[order](http://learnyouahaskell.com/higher-order-functions)
|
34
|
+
[functions](http://learnyousomeerlang.com/higher-order-functions). Put simply, a
|
35
|
+
higher order function is any function that can take one or more functions as
|
36
|
+
parameters and/or return a function as a result. Many functional languages,
|
37
|
+
such as Erlang, Haskell, and Closure, support higher order functions. Higher order
|
38
|
+
functions are a remarkable tool that can completely change the was software is
|
39
|
+
designed. Most classicaly object-oriented languages do not support higher
|
40
|
+
order functions. Unfortunately, Ruby does not directly support higher order
|
41
|
+
functions. Thanksfully, Ruby *does* give us blocks, `proc`s, and `lambda`s.
|
42
|
+
Though not strictly higher order functions, in most cases they are functionally
|
43
|
+
equivalent.
|
44
|
+
|
45
|
+
If you combine Ruby's ability to create functions sans-classes with the power
|
46
|
+
of blocks/`proc`s/`lambda`s, Ruby code can follow just about every modern functional
|
47
|
+
programming design paradigm. Hence, I consider Ruby to be a *multi-paradigm* language.
|
48
|
+
Add to this Ruby's vast metaprogramming capabilities and Ruby is easily one of the
|
49
|
+
most powerful languages in common use today.
|
31
50
|
|
32
51
|
This gem is my small and humble attempt to help Ruby reach its full potential as
|
33
|
-
a highly performant, functional programming language.
|
52
|
+
a highly performant, functional programming language. Virtually every function in
|
53
|
+
this library takes a block parameter. Some allow a block plus one or more `proc`
|
54
|
+
arguments. Most operate *on* data structures rather than being buried *in* data
|
55
|
+
structures. Finally, several of the tools in this library are Ruby implementations
|
56
|
+
of some of my favorite features from other functional programming languages. Not
|
57
|
+
every function is pure, but functions with side effects are easy to spot because
|
58
|
+
they almost always have names that end in an exclamation point.
|
59
|
+
|
60
|
+
My hope is that this gem will help Ruby programmers explore Ruby as a functional
|
61
|
+
language and improve their code in ways our object oriented brethern never
|
62
|
+
dreamed possible.
|
34
63
|
|
35
64
|
### Goals
|
36
65
|
|
@@ -48,8 +77,13 @@ My goal is to implement various functional programming patterns in Ruby. Specifi
|
|
48
77
|
|
49
78
|
Several features from Erlang, Go, and Clojure have been implemented thus far:
|
50
79
|
|
51
|
-
* Function overloading with Erlang-style [Pattern Matching](https://github.com/jdantonio/functional-ruby/blob/master/md/pattern_matching.md)
|
52
80
|
* Interface specifications with Erlang-style [Behavior](https://github.com/jdantonio/functional-ruby/blob/master/md/behavior.md)
|
81
|
+
* A [Catalog](https://github.com/jdantonio/functional-ruby/blob/master/md/catalog.md) class for managing sets of key/value pairs in a manner similar to Erlang's [proplists](http://www.erlang.org/doc/man/proplists.html)
|
82
|
+
* A toolkit of [collection](https://github.com/jdantonio/functional-ruby/blob/master/md/collection.md) utilities for operating on list-like data structures
|
83
|
+
* A set of string [inflections](https://github.com/jdantonio/functional-ruby/blob/master/md/inflect.md) borrowed from [Active Support](http://guides.rubyonrails.org/active_support_core_extensions.html#inflections)
|
84
|
+
* Function overloading with Erlang-style [Pattern Matching](https://github.com/jdantonio/functional-ruby/blob/master/md/pattern_matching.md)
|
85
|
+
* Tools for introspecting the runtime [Platform](https://github.com/jdantonio/functional-ruby/blob/master/md/platform.md) for information about the operating system and Ruby version
|
86
|
+
* [Search](https://github.com/jdantonio/functional-ruby/blob/master/md/search.md) and [sort](https://github.com/jdantonio/functional-ruby/blob/master/md/sort.md) algorithms like you remember from your algorithms class, but with a functional twist
|
53
87
|
* Several useful functional [Utilities](https://github.com/jdantonio/functional-ruby/blob/master/md/utilities.md)
|
54
88
|
|
55
89
|
### Is it any good?
|
data/lib/functional.rb
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
require 'functional/behavior'
|
2
|
+
require 'functional/behaviour'
|
3
|
+
require 'functional/catalog'
|
4
|
+
require 'functional/collection'
|
5
|
+
require 'functional/inflect'
|
2
6
|
require 'functional/pattern_matching'
|
3
7
|
require 'functional/platform'
|
8
|
+
require 'functional/search'
|
9
|
+
require 'functional/sort'
|
4
10
|
require 'functional/utilities'
|
5
11
|
require 'functional/version'
|
12
|
+
|
13
|
+
Infinity = 1/0.0 unless defined?(Infinity)
|
14
|
+
NaN = 0/0.0 unless defined?(NaN)
|
15
|
+
|
16
|
+
module Functional
|
17
|
+
|
18
|
+
class << self
|
19
|
+
include Collection
|
20
|
+
include Inflect
|
21
|
+
include Search
|
22
|
+
include Sort
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,487 @@
|
|
1
|
+
module Functional
|
2
|
+
|
3
|
+
# A collection of key/value pairs similar to a hash but ordered.
|
4
|
+
# Access is via index (like an array) rather than by key (like a
|
5
|
+
# hash). Supports duplicate keys. Indexing starts at zero.
|
6
|
+
class Catalog
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# Create a new Catalog from the given data. When +data+ is nil
|
10
|
+
# or an empty collection the resulting Catalog will be empty.
|
11
|
+
# When +data+ is an array, hash, or catalog array the appropriate
|
12
|
+
# +#from_+ factory method will be called. The +:from+ option is
|
13
|
+
# used to indicate the type of the source data.
|
14
|
+
#
|
15
|
+
# If a block is given each value in the from the source array will
|
16
|
+
# be passed to the block and the result will be stored as the value
|
17
|
+
# in the Catalog.
|
18
|
+
#
|
19
|
+
# @param [Array, Hash, Catalog] data the data to construct the
|
20
|
+
# Catalog from
|
21
|
+
# @param [Hash] opts processing options
|
22
|
+
#
|
23
|
+
# @option opts [Symbol] :from the type of the data source. Valid values
|
24
|
+
# are :catalog/:catalogue, :hash, :array (default :catalog).
|
25
|
+
def initialize(data=nil, opts={})
|
26
|
+
|
27
|
+
if block_given?
|
28
|
+
|
29
|
+
@data = []
|
30
|
+
data.each do |item|
|
31
|
+
@data << yield(item)
|
32
|
+
end
|
33
|
+
|
34
|
+
else
|
35
|
+
from = opts[:from]
|
36
|
+
from = :array if [:set, :list, :stack, :queue, :vector].include?(from)
|
37
|
+
from = "from_#{from}".to_sym
|
38
|
+
|
39
|
+
if Catalog.respond_to?(from)
|
40
|
+
@data = Catalog.send(from, data)
|
41
|
+
@data = @data.instance_variable_get(:@data)
|
42
|
+
elsif opts[:from].nil? && !data.nil?
|
43
|
+
@data = Catalog.from_catalog(data)
|
44
|
+
@data = @data.instance_variable_get(:@data)
|
45
|
+
else
|
46
|
+
@data = []
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Creates a new Catalog object from a hash. Each key/value pair in the
|
52
|
+
# hash will be converted to a key/value array in the new Catalog. If a
|
53
|
+
# block is given each value in the array will be passed to the block
|
54
|
+
# and the result will be stored as the value in the Catalog.
|
55
|
+
def self.from_hash(data = {})
|
56
|
+
collected = []
|
57
|
+
data.each do |key, value|
|
58
|
+
value = yield(value) if block_given?
|
59
|
+
collected << [key, value]
|
60
|
+
end
|
61
|
+
catalog = Catalog.new
|
62
|
+
catalog.instance_variable_set(:@data, collected)
|
63
|
+
return catalog
|
64
|
+
end
|
65
|
+
|
66
|
+
# Creates a new catalog object from an array. Each successive pair of
|
67
|
+
# elements will become a key/value pair in the new Catalog. If the source
|
68
|
+
# array has an odd number of elements the last element will be discarded.
|
69
|
+
# If a block is given each element in the source array will be passed to
|
70
|
+
# the block and the result will be stored in the new Catalog.
|
71
|
+
def self.from_array(*args)
|
72
|
+
collected = []
|
73
|
+
data = args.flatten
|
74
|
+
|
75
|
+
max = ((data.size % 2 == 0) ? data.size-1 : data.size-2)
|
76
|
+
(0..max).step(2) do |index|
|
77
|
+
key = block_given? ? yield(data[index]) : data[index]
|
78
|
+
value = block_given? ? yield(data[index+1]) : data[index+1]
|
79
|
+
collected << [key, value]
|
80
|
+
end
|
81
|
+
|
82
|
+
catalog = Catalog.new
|
83
|
+
catalog.instance_variable_set(:@data, collected)
|
84
|
+
return catalog
|
85
|
+
end
|
86
|
+
|
87
|
+
# Creates a new Catalog object from an array of key/value pairs.
|
88
|
+
# Each key/value pair in the source array will be stored in the new
|
89
|
+
# Catalog. If a block is given each value in the from the source array
|
90
|
+
# will be passed to the block and the result will be stored as the
|
91
|
+
# value in the Catalog.
|
92
|
+
def self.from_catalog(data, *args)
|
93
|
+
collected = []
|
94
|
+
|
95
|
+
if args.empty? && data.size == 2 && !data.first.is_a?(Array)
|
96
|
+
# Catalog.from_catalog([:one, 1])
|
97
|
+
data = [data]
|
98
|
+
elsif !args.empty?
|
99
|
+
#Catalog.from_catalog([:one, 1], [:two, 2], [:three, 3])
|
100
|
+
data = [data] + args
|
101
|
+
end
|
102
|
+
|
103
|
+
data.each do |item|
|
104
|
+
if block_given?
|
105
|
+
collected << [item.first, yield(item.last)]
|
106
|
+
else
|
107
|
+
collected << item
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
catalog = Catalog.new
|
112
|
+
catalog.instance_variable_set(:@data, collected)
|
113
|
+
return catalog
|
114
|
+
end
|
115
|
+
|
116
|
+
class << self
|
117
|
+
alias :from_catalogue :from_catalog
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns true if self array contains no elements.
|
121
|
+
def empty?
|
122
|
+
size == 0
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns the number of elements in self. May be zero.
|
126
|
+
def length
|
127
|
+
@data.length
|
128
|
+
end
|
129
|
+
|
130
|
+
alias :size :length
|
131
|
+
|
132
|
+
# Returns the first element, or the first n elements, of the array.
|
133
|
+
# If the array is empty, the first form returns nil, and the second
|
134
|
+
# form returns an empty array.
|
135
|
+
def first
|
136
|
+
@data.first
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns the last element(s) of self. If the array is empty,
|
140
|
+
# the first form returns nil.
|
141
|
+
def last
|
142
|
+
@data.last
|
143
|
+
end
|
144
|
+
|
145
|
+
# Equality—Two arrays are equal if they contain the same number of
|
146
|
+
# elements and if each element is equal to (according to Object.==)
|
147
|
+
# the corresponding element in the other array.
|
148
|
+
def ==(other)
|
149
|
+
if other.is_a? Catalog
|
150
|
+
return (@data == other.instance_variable_get(:@data))
|
151
|
+
elsif other.is_a? Array
|
152
|
+
return (@data == other)
|
153
|
+
else
|
154
|
+
return false
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
alias :eql? :==
|
159
|
+
|
160
|
+
# Comparison—Returns an integer (-1, 0, or +1) if this array is less
|
161
|
+
# than, equal to, or greater than other_ary. Each object in each
|
162
|
+
# array is compared (using <=>). If any value isn’t equal, then that
|
163
|
+
# inequality is the return value. If all the values found are equal,
|
164
|
+
# then the return is based on a comparison of the array lengths. Thus,
|
165
|
+
# two arrays are “equal” according to Array#<=> if and only if they have
|
166
|
+
# the same length and the value of each element is equal to the value of
|
167
|
+
# the corresponding element in the other array.
|
168
|
+
def <=>(other)
|
169
|
+
other = other.instance_variable_get(:@data) if other.is_a?(Catalog)
|
170
|
+
if other.is_a? Array
|
171
|
+
return @data <=> other
|
172
|
+
else
|
173
|
+
raise TypeError.new("can't convert #{other.class} into Catalog")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
alias :compare :<=>
|
178
|
+
alias :compare_to :<=>
|
179
|
+
|
180
|
+
# Returns a new array populated with the given objects.
|
181
|
+
def [](index)
|
182
|
+
datum = @data[index]
|
183
|
+
return (datum.nil? ? nil : datum.dup)
|
184
|
+
end
|
185
|
+
|
186
|
+
alias :at :[]
|
187
|
+
|
188
|
+
# Element Assignment—Sets the element at index, or replaces a subarray starting
|
189
|
+
# at start and continuing for length elements, or replaces a subarray specified
|
190
|
+
# by range. If indices are greater than the current capacity of the array, the
|
191
|
+
# array grows automatically. A negative indices will count backward from the end
|
192
|
+
# of the array. Inserts elements if length is zero. An IndexError is raised if a
|
193
|
+
# negative index points past the beginning of the array. See also Array#push,
|
194
|
+
# and Array#unshift.
|
195
|
+
def []=(index, value)
|
196
|
+
if (index >= 0 && index >= @data.size) || (index < 0 && index.abs > @data.size)
|
197
|
+
raise ArgumentError.new('index must reference an existing element')
|
198
|
+
elsif value.is_a?(Hash) && value.size == 1
|
199
|
+
@data[index] = [value.keys.first, value.values.first]
|
200
|
+
elsif value.is_a?(Array) && value.size == 2
|
201
|
+
@data[index] = value.dup
|
202
|
+
else
|
203
|
+
raise ArgumentError.new('value must be a one-element hash or a two-element array')
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Returns a string representation of Catalog.
|
208
|
+
def to_s
|
209
|
+
return @data.to_s
|
210
|
+
end
|
211
|
+
|
212
|
+
# Set Intersection—Returns a new array containing elements common to the two
|
213
|
+
# arrays, with no duplicates.
|
214
|
+
def &(other)
|
215
|
+
other = other.instance_variable_get(:@data) if other.is_a?(Catalog)
|
216
|
+
if other.is_a? Array
|
217
|
+
return Catalog.from_catalog(@data & other)
|
218
|
+
else
|
219
|
+
raise TypeError.new("can't convert #{other.class} into Catalog")
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
alias :intersection :&
|
224
|
+
|
225
|
+
# Concatenation—Returns a new array built by concatenating the two arrays
|
226
|
+
# together to produce a third array.
|
227
|
+
def +(other)
|
228
|
+
other = other.instance_variable_get(:@data) if other.is_a?(Catalog)
|
229
|
+
if other.is_a? Array
|
230
|
+
return Catalog.from_catalog(@data + other)
|
231
|
+
else
|
232
|
+
raise TypeError.new("can't convert #{other.class} into Catalog")
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
alias :add :+
|
237
|
+
alias :sum :+
|
238
|
+
|
239
|
+
# Set Union—Returns a new array by joining this array with other_array,
|
240
|
+
# removing duplicates.
|
241
|
+
def |(other)
|
242
|
+
other = other.instance_variable_get(:@data) if other.is_a?(Catalog)
|
243
|
+
if other.is_a? Array
|
244
|
+
return Catalog.from_catalog(@data | other)
|
245
|
+
else
|
246
|
+
raise TypeError.new("can't convert #{other.class} into Catalog")
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
alias :union :|
|
251
|
+
|
252
|
+
# Append—Pushes the given object(s) on to the end of this array.
|
253
|
+
# This expression returns the array itself, so several appends
|
254
|
+
# may be chained together.
|
255
|
+
def push(item)
|
256
|
+
if item.is_a?(Hash) && item.size == 1
|
257
|
+
@data << [item.keys.first, item.values.first]
|
258
|
+
return self
|
259
|
+
elsif item.is_a?(Array) && item.size == 2
|
260
|
+
@data << item
|
261
|
+
return self
|
262
|
+
else
|
263
|
+
raise TypeError.new("can't convert #{item.class} into Catalog")
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
alias :<< :push
|
268
|
+
alias :append :push
|
269
|
+
|
270
|
+
# Removes the last element from self and returns it, or nil if the
|
271
|
+
# Catalog is empty.
|
272
|
+
def pop
|
273
|
+
if self.empty?
|
274
|
+
return nil
|
275
|
+
else
|
276
|
+
return @data.pop
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# Copies the last element from self and returns it, or nil if the
|
281
|
+
# Catalog is empty.
|
282
|
+
def peek
|
283
|
+
if self.empty?
|
284
|
+
return nil
|
285
|
+
else
|
286
|
+
return @data.last.dup
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# Returns a new array populated with the keys from this hash.
|
291
|
+
# See also Hash#values.
|
292
|
+
def keys
|
293
|
+
return @data.collect{|item| item.first}
|
294
|
+
end
|
295
|
+
|
296
|
+
# Returns a new array populated with the values from hsh.
|
297
|
+
# See also Hash#keys.
|
298
|
+
def values
|
299
|
+
return @data.collect{|item| item.last}
|
300
|
+
end
|
301
|
+
|
302
|
+
# Calls block once for each key in hsh, passing the key and value
|
303
|
+
# to the block as a two-element array. Because of the assignment
|
304
|
+
# semantics of block parameters, these elements will be split out
|
305
|
+
# if the block has two formal parameters. Also see Hash.each_pair,
|
306
|
+
# which will be marginally more efficient for blocks with two
|
307
|
+
# parameters.
|
308
|
+
def each(&block)
|
309
|
+
@data.each do |item|
|
310
|
+
yield(item)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# Calls block once for each key in hsh, passing the key and value as parameters.
|
315
|
+
def each_pair(&block)
|
316
|
+
@data.each do |item|
|
317
|
+
yield(item.first, item.last)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# Calls block once for each key in hsh, passing the key as a parameter.
|
322
|
+
def each_key(&block)
|
323
|
+
@data.each do |item|
|
324
|
+
yield(item.first)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
# Calls block once for each key in hsh, passing the value as a parameter.
|
329
|
+
def each_value(&block)
|
330
|
+
@data.each do |item|
|
331
|
+
yield(item.last)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
# Returns true if the given object is present in self (that is,
|
336
|
+
# if any object == anObject), false otherwise.
|
337
|
+
def include?(key=nil, value=nil)
|
338
|
+
if key && value
|
339
|
+
return @data.include?([key, value])
|
340
|
+
elsif key.is_a?(Array)
|
341
|
+
return @data.include?(key)
|
342
|
+
elsif key.is_a?(Hash) && key.size == 1
|
343
|
+
return @data.include?([key.keys.first, key.values.first])
|
344
|
+
else
|
345
|
+
return false
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
# Element Reference—Returns the element at index, or returns a
|
350
|
+
# subarray starting at start and continuing for length elements,
|
351
|
+
# or returns a subarray specified by range. Negative indices count
|
352
|
+
# backward from the end of the array (-1 is the last element).
|
353
|
+
# Returns nil if the index (or starting index) are out of range.
|
354
|
+
def slice(index, length=nil)
|
355
|
+
if length.nil?
|
356
|
+
catalog = @data.slice(index)
|
357
|
+
else
|
358
|
+
catalog = @data.slice(index, length)
|
359
|
+
end
|
360
|
+
return Catalog.new(catalog)
|
361
|
+
end
|
362
|
+
|
363
|
+
# Deletes the element(s) given by an index (optionally with a length)
|
364
|
+
# or by a range. Returns the deleted object, subarray, or nil if the
|
365
|
+
# index is out of range.
|
366
|
+
def slice!(index, length=nil)
|
367
|
+
if length.nil?
|
368
|
+
catalog = @data.slice!(index)
|
369
|
+
else
|
370
|
+
catalog = @data.slice!(index, length)
|
371
|
+
end
|
372
|
+
return Catalog.new(catalog)
|
373
|
+
end
|
374
|
+
|
375
|
+
# Return a new Catalog created by sorting self according to the natural
|
376
|
+
# sort order of the keys.
|
377
|
+
def sort_by_key
|
378
|
+
sorted = @data.sort{|a, b| a.first <=> b.first}
|
379
|
+
return Catalog.new(sorted)
|
380
|
+
end
|
381
|
+
|
382
|
+
# Sort self according to the natural sort order of the keys. Returns self.
|
383
|
+
def sort_by_key!
|
384
|
+
sorted = @data.sort!{|a, b| a.first <=> b.first}
|
385
|
+
return self
|
386
|
+
end
|
387
|
+
|
388
|
+
# Return a new Catalog created by sorting self according to the natural
|
389
|
+
# sort order of the values.
|
390
|
+
def sort_by_value
|
391
|
+
sorted = @data.sort{|a, b| a.last <=> b.last}
|
392
|
+
return Catalog.new(sorted)
|
393
|
+
end
|
394
|
+
|
395
|
+
# Sort self according to the natural sort order of the values. Returns self.
|
396
|
+
def sort_by_value!
|
397
|
+
sorted = @data.sort!{|a, b| a.last <=> b.last}
|
398
|
+
return self
|
399
|
+
end
|
400
|
+
|
401
|
+
# Returns a new array created by sorting self. Comparisons for
|
402
|
+
# the sort will be done using the <=> operator or using an
|
403
|
+
# optional code block. The block implements a comparison between
|
404
|
+
# a and b, returning -1, 0, or +1. See also Enumerable#sort_by.
|
405
|
+
def sort(&block)
|
406
|
+
sorted = @data.sort(&block)
|
407
|
+
return Catalog.new(sorted)
|
408
|
+
end
|
409
|
+
|
410
|
+
# Sorts self. Comparisons for the sort will be done using the <=>
|
411
|
+
# operator or using an optional code block. The block implements a
|
412
|
+
# comparison between a and b, returning -1, 0, or +1.
|
413
|
+
# See also Enumerable#sort_by.
|
414
|
+
def sort!(&block)
|
415
|
+
sorted = @data.sort!(&block)
|
416
|
+
return self
|
417
|
+
end
|
418
|
+
|
419
|
+
# Returns a new array that is a one-dimensional flattening of self.
|
420
|
+
def to_a
|
421
|
+
return @data.flatten
|
422
|
+
end
|
423
|
+
|
424
|
+
# Returns a new hash by converting each key/value pair in self into
|
425
|
+
# a key/value pair in the hash. When duplicate keys are encountered
|
426
|
+
# the last value associated with that key is kept and the others are
|
427
|
+
# discarded.
|
428
|
+
def to_hash
|
429
|
+
catalog = {}
|
430
|
+
@data.each do |item|
|
431
|
+
catalog[item.first] = item.last
|
432
|
+
end
|
433
|
+
return catalog
|
434
|
+
end
|
435
|
+
|
436
|
+
# Returns a new array that is the dat equivalent of self where each
|
437
|
+
# key/value pair is an two-element array within the returned array.
|
438
|
+
def to_catalog
|
439
|
+
return @data.dup
|
440
|
+
end
|
441
|
+
|
442
|
+
alias :to_catalogue :to_catalog
|
443
|
+
|
444
|
+
# Deletes items from self that are equal to obj. If the item is
|
445
|
+
# not found, returns nil. If the optional code block is given,
|
446
|
+
# returns the result of block if the item is not found.
|
447
|
+
def delete(key, value=nil)
|
448
|
+
item = nil
|
449
|
+
|
450
|
+
if key && value
|
451
|
+
item = @data.delete([key, value])
|
452
|
+
elsif key.is_a? Array
|
453
|
+
item = @data.delete(key)
|
454
|
+
elsif key.is_a? Hash
|
455
|
+
item = @data.delete([key.keys.first, key.values.first])
|
456
|
+
end
|
457
|
+
|
458
|
+
item = yield if item.nil? && block_given?
|
459
|
+
return item
|
460
|
+
end
|
461
|
+
|
462
|
+
# Deletes the element at the specified index, returning that element,
|
463
|
+
# or nil if the index is out of range. See also Array#slice!.
|
464
|
+
def delete_at(index)
|
465
|
+
item = @data.delete_at(index)
|
466
|
+
item = yield if item.nil? && block_given?
|
467
|
+
return item
|
468
|
+
end
|
469
|
+
|
470
|
+
# Deletes every element of self for which block evaluates to true.
|
471
|
+
def delete_if(&block)
|
472
|
+
raise ArgumentError.new('no block supplied') unless block_given?
|
473
|
+
if block.arity <= 1
|
474
|
+
items = @data.delete_if(&block)
|
475
|
+
else
|
476
|
+
items = []
|
477
|
+
@data.each do |key, value|
|
478
|
+
items << [key, value] if yield(key, value)
|
479
|
+
end
|
480
|
+
items.each {|item| @data.delete(item)}
|
481
|
+
end
|
482
|
+
return self
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
class Catalogue < Catalog; end
|
487
|
+
end
|