Module: Enumerable
- Included in:
- Tuple
- Defined in:
- lib/core/facets/enumerable/per.rb,
lib/core/facets/enumerable/sum.rb,
lib/core/facets/enumerable/mash.rb,
lib/core/facets/enumerable/only.rb,
lib/core/facets/enumerable/pair.rb,
lib/core/facets/enumerable/apply.rb,
lib/core/facets/enumerable/defer.rb,
lib/core/facets/enumerable/every.rb,
lib/core/facets/enumerable/ewise.rb,
lib/core/facets/enumerable/graph.rb,
lib/core/facets/enumerable/hinge.rb,
lib/core/facets/enumerable/occur.rb,
lib/core/facets/enumerable/purge.rb,
lib/core/facets/enumerable/visit.rb,
lib/core/facets/enumerable/expand.rb,
lib/core/facets/enumerable/filter.rb,
lib/core/facets/enumerable/incase.rb,
lib/core/facets/enumerable/key_by.rb,
lib/core/facets/enumerable/map_by.rb,
lib/core/facets/enumerable/map_to.rb,
lib/core/facets/enumerable/cluster.rb,
lib/core/facets/enumerable/each_by.rb,
lib/core/facets/enumerable/exclude.rb,
lib/core/facets/enumerable/hashify.rb,
lib/core/facets/enumerable/squeeze.rb,
lib/core/facets/enumerable/uniq_by.rb,
lib/core/facets/enumerable/map_send.rb,
lib/core/facets/enumerable/map_with.rb,
lib/core/facets/enumerable/modulate.rb,
lib/core/facets/enumerable/value_by.rb,
lib/core/facets/enumerable/associate.rb,
lib/core/facets/enumerable/frequency.rb,
lib/core/facets/enumerable/accumulate.rb,
lib/core/facets/enumerable/find_yield.rb,
lib/core/facets/enumerable/compact_map.rb,
lib/core/facets/enumerable/organize_by.rb,
lib/core/facets/enumerable/recursively.rb,
lib/core/facets/enumerable/unassociate.rb,
lib/core/facets/enumerable/map_with_index.rb,
lib/standard/facets/thread.rb,
lib/standard/facets/enumargs.rb
Defined Under Namespace
Modules: Argumentable Classes: Hashifier, Recursor
Constant Summary collapse
- Arguments =
DEPRECATED: Module alias for old name.
Argumentable
Instance Method Summary collapse
-
#accumulate(iterations = 1) ⇒ Object
Accumulate a set of a set.
-
#accumulate_all(iterations = 1) ⇒ Object
Same as #accumulate, but does not apply #uniq to final result.
-
#apply ⇒ Object
Returns an elemental object.
-
#cluster ⇒ Object
Clusters together adjacent elements into a list of sub-arrays.
-
#compact_map(&block) ⇒ Object
(also: #compact_collect)
A more versitle #compact method.
-
#defer(&blk) ⇒ Object
Without a block: wrap the Enumerable object in such a way that map, select and similar operations are performed “horizontally” across a series of blocks, instead of building an array of results at each step.
-
#each_by(steps = nil, &block) ⇒ Object
Iterate through slices.
-
#every ⇒ Object
Returns an elemental object.
-
#every! ⇒ Object
In place version of #every.
-
#ewise(count = 1) ⇒ Object
(also: #elementwise)
Returns an elementwise Functor designed to make R-like elementwise operations possible.
-
#exclude?(object) ⇒ Boolean
The inverse of #include?.
-
#expand ⇒ Object
Expand all elements of an Enumerable object.
-
#filter(output = []) ⇒ Object
The block acts as an arbitrary filter on the data.
-
#find_yield(fallback = nil) ⇒ Object
(also: #map_detect)
Yield each element to the block and return the result of the block when that result evaluates as true, terminating early like #detect and #find.
-
#frequency ⇒ Object
Generates a hash mapping each unique symbol in the array to the absolute frequency it appears.
-
#graph(&yld) ⇒ Object
Like ‘#map`/`#collect`, but generates a Hash.
-
#hashify(val = nil, &block) ⇒ Object
The hashify methods is a higher-order message used to convert an enumerable object into a hash.
-
#hinge(init = {}) ⇒ Object
Apply each element of an enumerable ot a hash by iterating over each element and yielding the hash and element.
-
#incase?(what) ⇒ Boolean
The same as #include? but tested using #=== instead of #==.
-
#key_by ⇒ Object
Convert enumerable into a Hash, iterating over each member where the provided block must return the key to by used to map to the value.
-
#map_by ⇒ Object
Like #group_by, but maps the second value returned from the block.
-
#map_send(meth, *args, &block) ⇒ Object
Send a message to each element and collect the result.
-
#map_to(to_class) ⇒ Object
Map each element into another class via class constructor.
-
#map_with(*arrays, &block) ⇒ Hash
(also: #zip_map)
Combines #zip and #map in a single efficient operation.
-
#map_with_index ⇒ Object
(also: #collect_with_index)
Same as #collect but with an iteration counter.
-
#mash(&yld) ⇒ Object
Like ‘#map`/`#collect`, but generates a Hash.
-
#modulate(modulo) ⇒ Object
Divide an array into groups by modulo of the index.
-
#occur(n = nil) ⇒ Object
Returns an array of elements for the elements that occur n times.
-
#only ⇒ Object
Returns the only element in the enumerable.
-
#only? ⇒ Boolean
Does this Enumerable have the only element?.
-
#organize_by(&b) ⇒ Object
(also: #cluster_by)
Similar to #group_by but returns an array of the groups.
-
#pair(missing = nil) ⇒ Object
(also: #associate)
Like ‘each_slice(2)` but ensures the last element has a pair if odd sized.
-
#per(enum_method = nil, *enum_args) ⇒ Object
Per element meta-functor.
-
#purge(*trash, &block) ⇒ Object
A versitle compaction method.
-
#recursively(*types, &block) ⇒ Object
Returns a recursive functor, that allows enumerable methods to iterate through enumerable sub-elements.
-
#squeeze(*limited_to) ⇒ Object
Squeeze out the same elements.
-
#sum(*identity, &block) ⇒ Object
Uses #+ to sum the enumerated elements.
-
#threaded_map ⇒ Object
Like Enumerable#map but each iteration is processed via a separate thread.
-
#threaded_map_send(meth, *args, &block) ⇒ Object
Like Enumerable#map_send but each iteration is processed via a separate thread.
-
#unassociate(index = 1..-1)) ⇒ Object
Take an associative array and unassociate it.
-
#uniq_by ⇒ Object
Like #uniq, but determines uniqueness based on a given block.
-
#value_by ⇒ Object
Create a hash whose keys are the enumerable’s elements, with specified values.
-
#visit(opts = {}, &block) ⇒ Object
Recursively iterate over all Enumerable elements, or subset given :type=>[type1, type2, …].
Instance Method Details
#accumulate(iterations = 1) ⇒ Object
Accumulate a set of a set. For example, in an ORM design where ‘Group has_many User` we might have something equivalent to the following.
Group = Struct.new(:users)
User = Struct.new(:name, :friends)
user1 = User.new('John', [])
user2 = User.new('Jane', ['Jill'])
user3 = User.new('Joe' , ['Jack', 'Jim'])
group1 = Group.new([user1, user2])
group2 = Group.new([user2, user3])
groups = [group1, group2]
Now we can accumulate the users of all groups.
groups.accumulate.users #=> [user1, user2, user3]
You may pass an argument to perform chains, e.g. the following returns the names of users from all groups.
groups.accumulate(2).users.name #=> ['John','Jane','Joe']
Or we can gather all the friends of all users in groups.
groups.accumulate(2).users.friends #=> ['Jill','Jack','Jim']
This is more convenient then the equivalent.
groups.accumulate.users.accumulate.friends #=> ['Jill','Jack','Jim']
CREDIT: George Moshchovitis, Daniel Emirikol, Robert Dober
42 43 44 45 46 47 48 49 50 |
# File 'lib/core/facets/enumerable/accumulate.rb', line 42 def accumulate(iterations=1) return self if iterations == 0 Functor.new do |op, *args| result = [] each { |x| result << x.send(op, *args) } result.flatten.uniq.accumulate(iterations - 1) end end |
#accumulate_all(iterations = 1) ⇒ Object
Same as #accumulate, but does not apply #uniq to final result.
groups.accumulate_all(2).users.friends #=> ['Jill', 'Jill','Jack','Jim']
56 57 58 59 60 61 62 63 64 |
# File 'lib/core/facets/enumerable/accumulate.rb', line 56 def accumulate_all(iterations=1) return self if iterations == 0 Functor.new do |op, *args| result = [] each { |x| result << x.send(op, *args) } result.flatten.accumulate_all(iterations - 1) end end |
#apply ⇒ Object
Returns an elemental object. This allows you to map a method on to every element.
r = [1,2,3].apply.+
r #=> 6
12 13 14 15 16 17 |
# File 'lib/core/facets/enumerable/apply.rb', line 12 def apply #Functor.new do |sym, *args, &blk| # inject{ |r, e| r.__send__(sym, e, *args, &blk) } #end per(:inject) end |
#cluster ⇒ Object
Clusters together adjacent elements into a list of sub-arrays.
[2,2,2,3,3,4,2,2,1].cluster{ |x| x }
=> [[2, 2, 2], [3, 3], [4], [2, 2], [1]]
["dog", "duck", "cat", "dude"].cluster{ |x| x[0] }
=> [["dog", "duck"], ["cat"], ["dude"]]
13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/core/facets/enumerable/cluster.rb', line 13 def cluster cluster = [] each do |element| if cluster.last && yield(cluster.last.last) == yield(element) cluster.last << element else cluster << [element] end end cluster end |
#compact_map(&block) ⇒ Object Also known as: compact_collect
A more versitle #compact method. It can be used to collect and filter items out in one single step.
c = [1,2,3].compact_map do |n|
n < 2 ? nil : n
end
c #=> [2,3]
CREDIT: Trans
DEPRECATE: This method should probably be removed b/c #purge does almost the same thing and enum.map{}.compact works too.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/core/facets/enumerable/compact_map.rb', line 17 def compact_map(&block) y = [] if block_given? each do |*a| r = yield(*a) y << r unless r.nil? end else each do |r| y << r unless r.nil? end end y end |
#defer(&blk) ⇒ Object
Without a block: wrap the Enumerable object in such a way that map, select and similar operations are performed “horizontally” across a series of blocks, instead of building an array of results at each step. This reduces memory usage, allows partial results to be provided early, and permits working with infinite series.
a = (1..1_000_000_000).defer.select{ |i| i % 2 == 0 }.
map{ |i| i + 100 }.
take(10).to_a
With a block: the block acts as an arbitrary filter on the data. Unlike map, it can choose to drop elements from the result, and/or add additional ones. The first object passed to the block is the receiver of the output.
(1..1_000_000_000).
defer { |out,i| out << i if i % 2 == 0 }. # like select
defer { |out,i| out << i + 100 }. # like map
take(10).to_a
Use a method like to_a or to_h at the end of the chain when you want an Array or Hash built with the results, or each… if you just want to output each result and discard it.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/core/facets/enumerable/defer.rb', line 29 def defer(&blk) if block_given? Denumerator.new do |output| each do |*input| yield(output, *input) end end else Denumerator.new do |output| each do |*input| output.yield(*input) end end end end |
#each_by(steps = nil, &block) ⇒ Object
Iterate through slices. If slice steps
is not given, the arity of the block is used.
x = []
[1,2,3,4].each_by{ |a,b| x << [a,b] }
x #=> [ [1,2], [3,4] ]
x = []
[1,2,3,4,5,6].each_by(3){ |a| x << a }
x #=> [ [1,2,3], [4,5,6] ]
This is just like each_slice, except that it will check the arity of the block. If each_slice ever suppots this this method can be deprecated.
CREDIT: Trans
22 23 24 25 26 27 28 29 30 |
# File 'lib/core/facets/enumerable/each_by.rb', line 22 def each_by(steps=nil, &block) if steps each_slice(steps, &block) else steps = block.arity.abs each_slice(steps, &block) #each_slice(steps) {|i| block.call(*i)} end end |
#every ⇒ Object
Returns an elemental object. This allows you to map a method on to every element.
r = [1,2,3].every + 3
r #=> [4,5,6]
11 12 13 |
# File 'lib/core/facets/enumerable/every.rb', line 11 def every per(:map) end |
#every! ⇒ Object
In place version of #every.
17 18 19 20 |
# File 'lib/core/facets/enumerable/every.rb', line 17 def every! raise NoMethodError unless respond_to?(:map!) per(:map!) end |
#ewise(count = 1) ⇒ Object Also known as: elementwise
Returns an elementwise Functor designed to make R-like elementwise operations possible. This is very much like the #every method, but it treats array argument specially.
([1,2].ewise + 3) #=> [4,5]
Vector to vector
([1,2].ewise + [4,5]) #=> [5,7]
Special thanks to Martin DeMello for helping to develop this.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/core/facets/enumerable/ewise.rb', line 19 def ewise(count=1) Functor.new do |op,*args| if args.empty? r = self count.times do r = r.collect{ |a| a.send(op) } end r else r = args.collect do |arg| if Array === arg #arg.kind_of?(Enumerable) x = self count.times do ln = (arg.length > length ? length : arg.length ) x = x.slice(0...ln) x = x.zip(arg[0...ln]) x = x.collect{ |a,b| a.send(op,b) } #x = x.collect{ |a,b| b ? a.send(op,b) : nil } end x else x = self count.times do x = x.collect{ |a| a.send(op,arg) } end x end end r.flatten! if args.length == 1 r end end end |
#exclude?(object) ⇒ Boolean
The inverse of #include?.
[:a, :b].exclude?(:c) #=> true
[:a, :b].exclude?(:a) #=> false
10 11 12 |
# File 'lib/core/facets/enumerable/exclude.rb', line 10 def exclude?(object) !include?(object) end |
#expand ⇒ Object
Expand all elements of an Enumerable object.
[0, 2..3, 5..7]. #=> [0,[2, 3],[5,6,7]]
CREDIT: Trans
8 9 10 11 12 |
# File 'lib/core/facets/enumerable/expand.rb', line 8 def map do |x| (Enumerable === x ? x. : x) end end |
#filter(output = []) ⇒ Object
The block acts as an arbitrary filter on the data. Unlike map, it can choose to drop elements from the result and/or add additional elements. The first object passed to the block is the receiver of the output.
x = (1..10000)
x = x.filter{ |out,i| out << i if i % 2 == 0 } # like select
x = x.filter{ |out,i| out << i + 100 } # like map
x = x.take(3)
x #=> [102, 104, 106]
This is very similar to #each_with_object, but #filter handles argument better by reversing their order and using the splat operator. (This was also once known as #injecting.)
CREDIT: David Black, Louis J Scoras
21 22 23 24 25 26 27 28 29 30 |
# File 'lib/core/facets/enumerable/filter.rb', line 21 def filter(output=[]) #:yeild: if block_given? each do |*input| yield(output, *input) end output else to_enum(:filter) end end |
#find_yield(fallback = nil) ⇒ Object Also known as: map_detect
Yield each element to the block and return the result of the block when that result evaluates as true, terminating early like #detect and #find.
obj1 = Object.new
obj2 = Object.new
def obj1.foo?; false; end
def obj2.foo?; true ; end
def obj1.foo ; "foo1"; end
def obj2.foo ; "foo2"; end
[obj1, obj2].find_yield{ |obj| obj.foo if obj.foo? } #=> "foo2"
Another example.
[1,2,3,4,5].find_yield{ |i| j = i+1; j if j % 4 == 0 } #=> 4
If the block is never true, return the object given in the first parameter, or nil if none specified.
[1,2,3].find_yield{ |_| false } #=> nil
[false].find_yield(1){ |_| false } #=> 1
28 29 30 31 32 33 34 |
# File 'lib/core/facets/enumerable/find_yield.rb', line 28 def find_yield(fallback=nil) #:yield: each do |member| result = yield(member) return result if result end fallback end |
#frequency ⇒ Object
Generates a hash mapping each unique symbol in the array to the absolute frequency it appears.
[:a,:a,:b,:c,:c,:c].frequency #=> {:a=>2,:b=>1,:c=>3}
CREDIT: Brian Schröder
– NOTE: So why not use #inject here? e.g. …
inject(Hash.new(0)){|p,v| p[v]+=1; p}
Because it is a fair bit slower than the traditional definition. ++
18 19 20 21 |
# File 'lib/core/facets/enumerable/frequency.rb', line 18 def frequency p = Hash.new(0); each{ |v| p[v] += 1 }; p end |
#graph(&yld) ⇒ Object
Like ‘#map`/`#collect`, but generates a Hash. The block is expected to return two values: the key and the value for the new hash.
numbers = (1..3)
squares = numbers.graph{ |n| [n, n*n] } # { 1=>1, 2=>4, 3=>9 }
sq_roots = numbers.graph{ |n| [n*n, n] } # { 1=>1, 4=>2, 9=>3 }
CREDIT: Andrew Dudzik (adudzik), Trans
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/core/facets/enumerable/graph.rb', line 12 def graph(&yld) if yld h = {} each do |*kv| r = yld[*kv] case r when Hash nk, nv = *r.to_a[0] when Range nk, nv = r.first, r.last else nk, nv = *r end h[nk] = nv end h else Enumerator.new(self,:graph) end end |
#hashify(val = nil, &block) ⇒ Object
The hashify methods is a higher-order message used to convert an enumerable object into a hash. Converting an enumerable object into a hash is not a one-to-one conversion, for this reason #hashify is used to provide variant approches for the conversion most suited to the use case at hand. Here are some (but not a complete set of) examples.
If the enumerable is a collection of perfect pairs, like that which Hash#to_a generates, then #assoc can be used.
a = [ [:a,1], [:b,2] ]
a.hashify.assoc #=> { :a=>1, :b=>2 }
If it it contains only arrays, but are not perfect pairs, then #concat can be used.
a = [ [:a,1,2], [:b,2], [:c], [:d] ]
a.hashify.concat #=> { :a=>[1,2], :b=>[2], :c=>[], :d=>[] }
If the array contains objects other then arrays then the #splat method might do the trick.
a = [ [:a,1,2], 2, :b, [:c,3], 9 ]
a.hashify.splat #=> { [:a,1,2]=>2, :b=>[:c,3], 9=>nil }
Also, the particular dispatch can be left up the Hashify using the #auto method. See Hashify#auto for details on this.
TODO: This method takes arguments only for the sake of the old method which has been deprecated. These will be removed eventually.
CREDIT: Robert Klemme, Sandor Szücs, Trans
40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/core/facets/enumerable/hashify.rb', line 40 def hashify(val=nil, &block) if val warn "The old Enumerable#hashify method has been be deprecated. Use #value_by instead." value_by{ val } end if block warn "The old Enumerable#hashify method has been be deprecated. Use #value_by instead." value_by(&block) end Hashifier.new(self) end |
#hinge(init = {}) ⇒ Object
Apply each element of an enumerable ot a hash by iterating over each element and yielding the hash and element.
[1,2,3].hinge{|h,e| h[e] = e+1 }
#=> {1=>2, 2=>3, 3=>4}
TODO: Enumerable#hinge will get a new name.
12 13 14 15 16 |
# File 'lib/core/facets/enumerable/hinge.rb', line 12 def hinge(init={}) h = init each{ |v| yield(h,v) } h end |
#incase?(what) ⇒ Boolean
The same as #include? but tested using #=== instead of #==.
[1, 2, "a"].incase?(2) #=> true
[1, 2, "a"].incase?(String) #=> true
[1, 2, "a"].incase?(3) #=> false
Why the name ‘incase`? Because the method uses case-equality. Along with the alliteration for “in case” and the similarity with “include?”, it seemed like the perfect fit.
15 16 17 |
# File 'lib/core/facets/enumerable/incase.rb', line 15 def incase?(what) any? { |x| what === x } end |
#key_by ⇒ Object
Convert enumerable into a Hash, iterating over each member where the provided block must return the key to by used to map to the value.
Examples:
[:a,:b,:c].key_by{ |v| v.to_s }
#=> {'a'=>:a, 'b'=>:b, 'c'=>:c}
TODO: How should this method behave with a Hash?
Returns: Hash
16 17 18 19 20 21 22 23 24 25 |
# File 'lib/core/facets/enumerable/key_by.rb', line 16 def key_by return to_enum(:key_by) unless block_given? h = {} each do |v| h[yield(v)] = v end return h end |
#map_by ⇒ Object
Like #group_by, but maps the second value returned from the block.
a = [1,2,3,4,5]
a.map_by{ |e| [e % 2, e + 1] }
#=> { 0=>[3,5], 1=>[2,4,6] }
Works well with a hash too.
h = {"A"=>1, "B"=>1, "C"=>1, "D"=>2, "E"=>2}
h.map_by{ |k,v| [v, k.downcase] }
#=> {1=>["a", "b", "c"], 2=>["d", "e"]}
If a second value is not returned, #map_by acts like #group_by.
h = {"A"=>1, "B"=>1, "C"=>1, "D"=>2, "E"=>2}
h.map_by{ |k,v| v }
#=> {1=>[["A",1], ["B",1], ["C",1]], 2=>[["D",2], ["E",2]]}
21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/core/facets/enumerable/map_by.rb', line 21 def map_by #:yield: res = {} each do |a| k,v = yield(*a) if v (res[k] ||= []) << v else (res[k] ||= []) << a end end res end |
#map_send(meth, *args, &block) ⇒ Object
Send a message to each element and collect the result.
[1,2,3].map_send(:+, 3) #=> [4,5,6]
CREDIT: Sean O’Halpin
9 10 11 |
# File 'lib/core/facets/enumerable/map_send.rb', line 9 def map_send(meth, *args, &block) map{|e| e.send(meth, *args, &block)} end |
#map_to(to_class) ⇒ Object
Map each element into another class via class constructor.
8 9 10 |
# File 'lib/core/facets/enumerable/map_to.rb', line 8 def map_to(to_class) map{ |e| to_class.new(e) } end |
#map_with(*arrays, &block) ⇒ Hash Also known as: zip_map
Combines #zip and #map in a single efficient operation.
h = {}
[1,2,3].map_with [:x,:y,:z] do |n,k|
h[k] = n
end
h #=> {:x=>1, :y=>2, :z=>3}
14 15 16 |
# File 'lib/core/facets/enumerable/map_with.rb', line 14 def map_with(*arrays, &block) enum_for(:zip, *arrays).map(&block) end |
#map_with_index ⇒ Object Also known as: collect_with_index
Same as #collect but with an iteration counter.
a = [1,2,3].collect_with_index { |e,i| e*i }
a #=> [0,2,6]
CREDIT: Gavin Sinclair
10 11 12 13 14 15 16 |
# File 'lib/core/facets/enumerable/map_with_index.rb', line 10 def map_with_index r = [] each_with_index do |e, i| r << yield(e, i) end r end |
#mash(&yld) ⇒ Object
Like ‘#map`/`#collect`, but generates a Hash. The block is expected to return two values: the key and the value for the new hash.
numbers = (1..3)
squares = numbers.mash{ |n| [n, n*n] } # { 1=>1, 2=>4, 3=>9 }
sq_roots = numbers.mash{ |n| [n*n, n] } # { 1=>1, 4=>2, 9=>3 }
CREDIT: Andrew Dudzik (adudzik), Trans
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/core/facets/enumerable/mash.rb', line 12 def mash(&yld) if yld h = {} each do |*kv| r = yld[*kv] case r when Hash nk, nv = *r.to_a[0] when Range nk, nv = r.first, r.last else nk, nv = *r end h[nk] = nv end h else Enumerator.new(self,:graph) end end |
#modulate(modulo) ⇒ Object
Divide an array into groups by modulo of the index.
[2,4,6,8].modulate(2) #=> [[2,6],[4,8]]
CREDIT: Trans
NOTE: Would the better name for this be ‘collate’?
11 12 13 14 15 16 17 18 19 |
# File 'lib/core/facets/enumerable/modulate.rb', line 11 def modulate(modulo) return to_a if modulo == 1 raise ArgumentError, 'bad modulo' if size % modulo != 0 r = Array.new(modulo, []) (0...size).each do |i| r[i % modulo] += [self[i]] end r end |
#occur(n = nil) ⇒ Object
Returns an array of elements for the elements that occur n times. Or according to the results of a given block.
a = [1,1,2,3,3,4,5,5]
a.occur(1).sort #=> [2,4]
a.occur(2).sort #=> [1,3,5]
a.occur(3).sort #=> []
a.occur(1..1).sort #=> [2,4]
a.occur(2..3).sort #=> [1,3,5]
a.occur { |n| n == 1 }.sort #=> [2,4]
a.occur { |n| n > 1 }.sort #=> [1,3,5]
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/core/facets/enumerable/occur.rb', line 18 def occur(n=nil) #:yield: result = Hash.new { |hash, key| hash[key] = Array.new } each do |item| key = item result[key] << item end if block_given? result.reject! { |key, values| ! yield(values.size) } else raise ArgumentError unless n if Range === n result.reject! { |key, values| ! n.include?(values.size) } else result.reject! { |key, values| values.size != n } end end return result.values.flatten.uniq end |
#only ⇒ Object
Returns the only element in the enumerable. Raises an IndexError if the enumreable has more then one element.
[5].only # => 5
expect IndexError do
[1,2,3].only
end
expect IndexError do
[].only
end
CREDIT: Lavir the Whiolet, Gavin Sinclair, Noah Gibbs
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/core/facets/enumerable/only.rb', line 18 def only first = false first_item = nil each do |item| if first raise IndexError, "not the only element of enumerable" else first = true first_item = item end end if first return first_item else raise IndexError, "not the only element of enumerable" end end |
#only? ⇒ Boolean
Does this Enumerable have the only element?
It differs from Enumerable#one? in that it does not check the items themselves. It checks the quantity only.
CREDIT: Lavir the Whiolet
45 46 47 48 49 50 51 52 53 54 |
# File 'lib/core/facets/enumerable/only.rb', line 45 def only? first = false each do |item| return false if first first = true end return first end |
#organize_by(&b) ⇒ Object Also known as: cluster_by
Similar to #group_by but returns an array of the groups. Returned elements are sorted by block.
%w{this is a test}.organize_by {|x| x[0]}
#=> [ ['a'], ['is'], ['this', 'test'] ]
CREDIT: Erik Veenstra
11 12 13 |
# File 'lib/core/facets/enumerable/organize_by.rb', line 11 def organize_by(&b) group_by(&b).sort.transpose.pop || [] # group_by(&b).values ? end |
#pair(missing = nil) ⇒ Object Also known as: associate
Like ‘each_slice(2)` but ensures the last element has a pair if odd sized.
[:a,1,:b,2,:c,3].pair.to_a #=> [[:a,1],[:b,2],[:c,3]]
8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/core/facets/enumerable/pair.rb', line 8 def pair(missing=nil) return to_enum(:pair) unless block_given? each_slice(2) do |kv| if kv.size == 1 yield kv.first, missing else yield kv.first, kv.last end end end |
#per(enum_method = nil, *enum_args) ⇒ Object
Per element meta-functor.
([1,2,3].per(:map) + 3) #=> [4,5,6]
([1,2,3].per(:select) > 1) #=> [2,3]
Using fluid notation.
([1,2,3].per.map + 3) #=> [4,5,6]
([1,2,3].per.select > 1) #=> [2,3]
21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/core/facets/enumerable/per.rb', line 21 def per(enum_method=nil, *enum_args) if enum_method Functor.new do |op, *args, &blk| __send__(enum_method || :map, *enum_args){ |x, *y| x.__send__(op, *y, *args, &blk) } end else Functor.new do |enumr_method, *enumr_args| Functor.new do |op, *args, &blk| __send__(enumr_method, *enumr_args){ |x, *y| x.__send__(op, *y, *args, &blk) } end end end end |
#purge(*trash, &block) ⇒ Object
A versitle compaction method. Like #map but used to filter out multiple items in a single step.
Without trash
arguments nil
is assumed.
[1, nil, 2].purge #=> [1,2]
If trash
arguments are given, each argument is compared for a match using #==.
(1..6).purge(3,4) #=> [1,2,5,6]
If a block is given, the yield is used in the matching condition instead of the element itsef.
(1..6).purge(0){ |n| n % 2 } #=> [1,3,5]
NOTE: This could just as well be an override of the core #compact method, but to avoid potential issues associated with overriding core methods we use the alternate name #purge.
CREDIT: Trans
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/core/facets/enumerable/purge.rb', line 27 def purge(*trash, &block) trash = [nil] if trash.empty? r = [] if block_given? each do |e| y = yield(e) r << e unless trash.any?{|t| t == y} end else each do |e| r << e unless trash.any?{|t| t == e} end end r end |
#recursively(*types, &block) ⇒ Object
Returns a recursive functor, that allows enumerable methods to iterate through enumerable sub-elements. By default it only recurses over elements of the same type.
7 8 9 |
# File 'lib/core/facets/enumerable/recursively.rb', line 7 def recursively(*types, &block) Recursor.new(self, *types, &block) end |
#squeeze(*limited_to) ⇒ Object
Squeeze out the same elements. This behaves like C++ unique(), removing equivalent elements that are concomitant to each other. To get a similar result with Array#uniq, the array would have to be sorted first.
Calculation order is O(n).
Examples
[1,2,2,3,3,2,1].squeeze #=> [1,2,3,2,1]
[1,2,2,3,3,2,1].sort.squeeze #=> [1,2,3]
[1,2,2,3,3,2,1].squeeze(*[3]) #=> [1,2,2,3,2,1]
Returns [Array].
CREDIT: T. Yamada
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/core/facets/enumerable/squeeze.rb', line 20 def squeeze(*limited_to) first = true r = [] # result c = nil # current each do |e| if !limited_to.empty? && !limited_to.include?(e) r << e elsif first || c != e r << e first = false c = e end end r end |
#sum(*identity, &block) ⇒ Object
Uses #+ to sum the enumerated elements.
[1,2,3].sum #=> 6
[3,3,3].sum #=> 9
Note that Facets’ sum method is completely generic – it can work on any objects that respond to #+.
[[1],[2],[3]].sum #=> [1,2,3]
For this reason it is usually a good idea to provide a default value. Consider the difference between the two expressions below.
[].sum #=> nil
[].sum(0) #=> 0
This default value also acts as an initial value.
[].sum(5) #=> 5
[1,2,3].sum(10) #=> 16
A block can also be passed to coax the elements before summation.
[1.1,2.2,3.3].sum(10.4, &:to_i) #=> 16.4
Notice the initial value is not effected by the block.
32 33 34 35 36 37 38 |
# File 'lib/core/facets/enumerable/sum.rb', line 32 def sum(*identity, &block) if block_given? map(&block).sum(*identity) else reduce(*identity) { |sum, element| sum + element } end end |
#threaded_map ⇒ Object
Like Enumerable#map but each iteration is processed via a separate thread.
CREDIT: Sean O’Halpin
59 60 61 |
# File 'lib/standard/facets/thread.rb', line 59 def threaded_map #:yield: map{ |e| Thread.new(e){ |t| yield(t) } }.map{ |t| t.value } end |
#threaded_map_send(meth, *args, &block) ⇒ Object
Like Enumerable#map_send but each iteration is processed via a separate thread.
CREDIT: Sean O’Halpin
68 69 70 |
# File 'lib/standard/facets/thread.rb', line 68 def threaded_map_send(meth, *args, &block) map{ |e| Thread.new(e){ |t| t.send(meth, *args, &block) } }.map{ |t| t.value } end |
#unassociate(index = 1..-1)) ⇒ Object
Take an associative array and unassociate it.
[[:a,1], [:b,2]].unassociate.to_a #=> [:a, [1], :b, [2]]
[[:a,1], [:b,2]].unassociate(1).to_a #=> [:a, 1, :b, 2]
8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/core/facets/enumerable/unassociate.rb', line 8 def unassociate(index = 1..-1) return to_enum(:unassociate, index) unless block_given? each do |v| case v when Array yield v[0] yield v[index] else yield v yield nil end end end |
#uniq_by ⇒ Object
Like #uniq, but determines uniqueness based on a given block.
(-5..5).to_a.uniq_by {|i| i*i }
#=> [-5, -4, -3, -2, -1, 0]
8 9 10 |
# File 'lib/core/facets/enumerable/uniq_by.rb', line 8 def uniq_by #:yield: h = {}; inject([]) {|a,x| h[yield(x)] ||= a << x} end |
#value_by ⇒ Object
Create a hash whose keys are the enumerable’s elements, with specified values.
If no block is given, the given parameter (default true) is used for all values, e.g.:
[1,2,3].value_by{ true } #=> {1=>true, 2=>true, 3=>true}
[1,2,3].value_by{ "a" } #=> {1=>"a", 2=>"a", 3=>"a"}
If a block is given, each key’s value is the result of running the block for that key, e.g.:
[1,2,3].value_by{ |n| "a"*n } #=> {1=>"a", 2=>"aa", 3=>"aaa"}
18 19 20 21 22 23 24 |
# File 'lib/core/facets/enumerable/value_by.rb', line 18 def value_by return to_enum(:value_by) unless block_given? h = {} each { |item| h[item] = yield(item) } h end |
#visit(opts = {}, &block) ⇒ Object
Recursively iterate over all Enumerable elements, or subset given :type=>[type1, type2, …].
[1, 2, 8..9].visit{ |x| x.succ }
# => [2, 3, [9, 10]]
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/core/facets/enumerable/visit.rb', line 9 def visit(opts={}, &block) type = opts[:type] ? [opts[:type]].flatten : [Enumerable] skip = opts[:skip] map do |v| case v when String # b/c of 1.8 block.call(v) when *type v.visit(opts, &block) else if skip && Enumerable === v v else block.call(v) end end end end |