lazily 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -41,14 +41,14 @@ See how it printed all the numbers from 1 to 10, indicating that the block given
41
41
  4
42
42
  => [1, 4, 9, 16]
43
43
 
44
- Same result, but notice how the block was only evaluated four times.
44
+ Same result, but notice how the block was only evaluated four times.
45
45
 
46
46
  Lazy pipelines
47
47
  --------------
48
48
 
49
49
  By combining two or more lazy operations, you can create an efficient "pipeline", e.g.
50
50
 
51
- User.to_enum(:find_each).lazily.select do |u|
51
+ User.to_enum(:find_each).lazily.select do |u|
52
52
  u.first_name[0] == u.last_name[0]
53
53
  end.collect(&:company).uniq.to_a
54
54
 
@@ -73,7 +73,7 @@ This is analogous to a Unix shell pipeline, though of course here we're talking
73
73
  Lazy multi-threaded processing
74
74
  ------------------------------
75
75
 
76
- The `#in_threads` method is a multi-threaded version of `#collect`, allowing multiple elements of a collection to be processed in parallel. It requires a numeric argument specifying the maximum number of Threads to use.
76
+ The `#in_threads` method is a multi-threaded version of `#collect`, allowing multiple elements of a collection to be processed in parallel. It requires a numeric argument specifying the maximum number of Threads to use.
77
77
 
78
78
  Benchmark.realtime do
79
79
  [1,2,3,4].lazily.in_threads(10) do |x|
@@ -82,7 +82,7 @@ The `#in_threads` method is a multi-threaded version of `#collect`, allowing mul
82
82
  end.to_a #=> [2,4,6,8]
83
83
  end.to_i #=> 1
84
84
 
85
- Outputs will be yielded in the expected order, making it a drop-in replacement for `#collect`.
85
+ Outputs will be yielded in the expected order, making it a drop-in replacement for `#collect`.
86
86
 
87
87
  Unlike some other "parallel map" implementations, the output of `#in_threads` is lazy (though it does need to pre-fetch elements from the source collection as required to start Threads).
88
88
 
@@ -116,10 +116,24 @@ A block can be provided to determine the sort-order.
116
116
  Lazily.merge(array1, array2) { |x| x.length }
117
117
  #=> %w(a dd eee cccc bbbbb)
118
118
 
119
+ `Lazily.associate` matches up "like" elements from separate collections. Again, it assumes it's inputs sorted.
120
+
121
+ fruit = %w(apple banana orange)
122
+ nautical_terms = %w(anchor boat flag)
123
+ Lazily.associate(:fruit => fruit, :nautical => nautical_terms) do |word|
124
+ word.chars.first
125
+ end
126
+ #=> [
127
+ { :fruit => ["apple"], :nautical => ["anchor"] },
128
+ { :fruit => ["banana"], :nautical => ["boat"] },
129
+ { :fruit => [], :nautical => ["flag"] },
130
+ { :fruit => ["orange"], :nautical => [] }
131
+ ]
132
+
119
133
  Same but different
120
134
  ------------------
121
135
 
122
- There are numerous similar implementations of lazy operations on Enumerables.
136
+ There are numerous similar implementations of lazy operations on Enumerables.
123
137
 
124
138
  ### Lazily vs. Enumerating
125
139
 
@@ -0,0 +1,44 @@
1
+ require "lazily/enumerable"
2
+
3
+ module Lazily
4
+
5
+ class << self
6
+
7
+ # Associate "like" elements of two or more Enumerables.
8
+ #
9
+ # @param labelled_enumerables [Hash<key,Enumerable>]
10
+ # @return [Enumerable] a lazy collection of Hashes
11
+ #
12
+ # @example
13
+ # Lazily.associate(a: [1,2,4], b: [1,3,4,4])
14
+ # #=> [
15
+ # { a: [1], b: [1] },
16
+ # { a: [2] },
17
+ # { b: [3] },
18
+ # { a: [4], b: [4, 4] }
19
+ # ]
20
+ #
21
+ def associate(source_map, &block)
22
+ labels = source_map.keys
23
+ tagged_element_sources = source_map.map do |label, enumerable|
24
+ enumerable.lazily.map do |value|
25
+ key = block ? block.call(value) : value
26
+ TaggedElement.new(key, value, label)
27
+ end
28
+ end
29
+ tagged_elements = Lazily.merge(*tagged_element_sources) { |te| te.key }
30
+ tagged_elements.chunk { |te| te.key }.map do |_, tagged_elements|
31
+ association = {}
32
+ labels.each { |label| association[label] = [] }
33
+ tagged_elements.each do |te|
34
+ association[te.label] << te.value
35
+ end
36
+ association
37
+ end
38
+ end
39
+
40
+ TaggedElement = Struct.new(:key, :value, :label)
41
+
42
+ end
43
+
44
+ end
@@ -1,3 +1,4 @@
1
+ require "lazily/associating"
1
2
  require "lazily/concatenating"
2
3
  require "lazily/merging"
3
4
  require "lazily/zipping"
@@ -1,3 +1,3 @@
1
1
  module Lazily
2
- VERSION = "0.1.2".freeze
2
+ VERSION = "0.2.0".freeze
3
3
  end
@@ -0,0 +1,96 @@
1
+ require "spec_helper"
2
+
3
+ describe Lazily, "associating" do
4
+
5
+ describe ".associate" do
6
+
7
+ context "with two identical Enumerables" do
8
+
9
+ let(:collectionA) { [2,4,6] }
10
+ let(:collectionB) { collectionA }
11
+
12
+ let(:result) do
13
+ Lazily.associate(:A => collectionA, :B => collectionB)
14
+ end
15
+
16
+ it "yields matched pairs" do
17
+ expect(result.to_a).to eq [
18
+ { :A => [2], :B => [2] },
19
+ { :A => [4], :B => [4] },
20
+ { :A => [6], :B => [6] }
21
+ ]
22
+ end
23
+
24
+ end
25
+
26
+ context "with three identical Enumerables" do
27
+
28
+ let(:collectionA) { [1,2]}
29
+ let(:collectionB) { collectionA }
30
+ let(:collectionC) { collectionA }
31
+
32
+ let(:result) do
33
+ Lazily.associate(:A => collectionA, :B => collectionB, :C => collectionC)
34
+ end
35
+
36
+ it "yields matched triples" do
37
+ expect(result.to_a).to eq [
38
+ { :A => [1], :B => [1], :C => [1] },
39
+ { :A => [2], :B => [2], :C => [2] }
40
+ ]
41
+ end
42
+
43
+ end
44
+
45
+ context "when elements are missing" do
46
+
47
+ let(:collectionA) { [1,2] }
48
+ let(:collectionB) { [2,3] }
49
+ let(:collectionC) { [1,3] }
50
+
51
+ let(:result) do
52
+ Lazily.associate(:A => collectionA, :B => collectionB, :C => collectionC)
53
+ end
54
+
55
+ it "returns an empty Array for the corresponding label" do
56
+ expect(result.to_a).to eq [
57
+ { :A => [1], :B => [ ], :C => [1] },
58
+ { :A => [2], :B => [2], :C => [ ] },
59
+ { :A => [ ], :B => [3], :C => [3] }
60
+ ]
61
+ end
62
+
63
+ end
64
+
65
+ context "with a block" do
66
+
67
+ let(:colours) { %w(blue red) }
68
+ let(:fruits) { %w(apple banana cranberry) }
69
+ let(:shapes) { %w(circle rectangle triangle) }
70
+
71
+ let(:result) do
72
+ Lazily.associate(:c => colours, :f => fruits, :s => shapes) do |word|
73
+ word.chars.first
74
+ end
75
+ end
76
+
77
+ it "uses the block to associate the elements" do
78
+ expect(result.to_a).to eq [
79
+ { :c => [], :f => ['apple'], :s => [] },
80
+ { :c => ['blue'], :f => ['banana'], :s => [] },
81
+ { :c => [], :f => ['cranberry'], :s => ['circle'] },
82
+ { :c => ['red'], :f => [], :s => ['rectangle'] },
83
+ { :c => [], :f => [], :s => ['triangle'] }
84
+ ]
85
+ end
86
+
87
+ end
88
+
89
+ it "is lazy" do
90
+ result = Lazily.associate(:a => [1,2].ecetera, :b => [3,4].ecetera )
91
+ expect(result.first).to eq(:a => [1], :b => [])
92
+ end
93
+
94
+ end
95
+
96
+ end if ::Enumerable.method_defined?(:chunk)
@@ -16,27 +16,27 @@ describe Lazily, "threading" do
16
16
  [1,2,3].ecetera.lazily.in_threads(2) { |x| x * 2 }.should be_lazy
17
17
  end
18
18
 
19
- def round(n, accuracy = 0.02)
20
- (n / accuracy).round.to_f * accuracy
19
+ def round(n, accuracy = 20)
20
+ (n * accuracy).round.to_f / accuracy
21
21
  end
22
22
 
23
23
  it "runs the specified number of threads in parallel" do
24
- delays = [0.03, 0.03, 0.03]
24
+ delays = [0.05, 0.05, 0.05]
25
25
  start = Time.now
26
26
  delays.lazily.in_threads(2) do |delay|
27
27
  sleep(delay)
28
28
  end.to_a
29
- round(Time.now - start).should eq(0.06)
29
+ round(Time.now - start).should eq(0.1)
30
30
  end
31
31
 
32
32
  it "acts as a sliding window" do
33
- delays = [0.1, 0.08, 0.06, 0.04, 0.02]
33
+ delays = [0.1, 0.15, 0.05, 0.05, 0.05]
34
34
  start = Time.now
35
35
  elapsed_times = delays.lazily.in_threads(3) do |delay|
36
36
  sleep(delay)
37
37
  round(Time.now - start)
38
38
  end
39
- elapsed_times.to_a.should eq([0.1, 0.08, 0.06, 0.14, 0.12])
39
+ elapsed_times.to_a.should eq([0.1, 0.15, 0.05, 0.15, 0.2])
40
40
  end
41
41
 
42
42
  it "surfaces exceptions" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lazily
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-24 00:00:00.000000000 Z
12
+ date: 2013-07-14 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: ! " Lazily implements \"lazy\" versions of many Enumerable methods,\n
15
15
  \ allowing streamed processing of large (or even infinite) collections.\n\n It's
@@ -30,6 +30,7 @@ files:
30
30
  - benchmarks/pipeline_bench.rb
31
31
  - lazily.gemspec
32
32
  - lib/lazily.rb
33
+ - lib/lazily/associating.rb
33
34
  - lib/lazily/combining.rb
34
35
  - lib/lazily/concatenating.rb
35
36
  - lib/lazily/enumerable.rb
@@ -40,6 +41,7 @@ files:
40
41
  - lib/lazily/threading.rb
41
42
  - lib/lazily/version.rb
42
43
  - lib/lazily/zipping.rb
44
+ - spec/lazily/associating_spec.rb
43
45
  - spec/lazily/bugs_spec.rb
44
46
  - spec/lazily/concatenating_spec.rb
45
47
  - spec/lazily/filtering_spec.rb
@@ -63,7 +65,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
63
65
  version: '0'
64
66
  segments:
65
67
  - 0
66
- hash: 1657751678053576424
68
+ hash: 3722161259490547720
67
69
  required_rubygems_version: !ruby/object:Gem::Requirement
68
70
  none: false
69
71
  requirements:
@@ -72,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
74
  version: '0'
73
75
  segments:
74
76
  - 0
75
- hash: 1657751678053576424
77
+ hash: 3722161259490547720
76
78
  requirements: []
77
79
  rubyforge_project:
78
80
  rubygems_version: 1.8.23
@@ -80,6 +82,7 @@ signing_key:
80
82
  specification_version: 3
81
83
  summary: Lazy Enumerables for everybody!
82
84
  test_files:
85
+ - spec/lazily/associating_spec.rb
83
86
  - spec/lazily/bugs_spec.rb
84
87
  - spec/lazily/concatenating_spec.rb
85
88
  - spec/lazily/filtering_spec.rb