filter_matcher 0.0.1 → 0.1.0
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.
- data/README.md +79 -37
- data/lib/filter_matcher/filter_matcher.rb +28 -16
- data/lib/filter_matcher/version.rb +1 -1
- data/test/people_matcher.rb +49 -0
- data/test/people_matcher_test.rb +1 -1
- metadata +4 -3
- data/lib/filter_matcher/people_matcher.rb +0 -71
data/README.md
CHANGED
@@ -36,43 +36,63 @@ First we need to specify how we want to filter our data to find the best match.
|
|
36
36
|
|
37
37
|
Lets say the most important is the name. If name filter finds a single result then this is a match. We can define the filter like:
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
m.filter do |col|
|
40
|
+
col.select { |element| element[:name] == name }
|
41
41
|
end
|
42
42
|
|
43
|
-
The filter has to be named in the convention: <what_it_filters>_filter
|
44
|
-
|
45
43
|
If this is not sufficient we run another filter on the result of the previous one. Lets define another filter, saying that the second most important thing to find a match is age.
|
46
44
|
|
47
|
-
|
48
|
-
|
45
|
+
m.filter do |col|
|
46
|
+
col.select { |element| element[:age] == age}
|
49
47
|
end
|
50
48
|
|
51
49
|
Simple and very similar to the previous one. Filters can have any logic you need. Lets define the last one - homepage filter.
|
52
50
|
|
53
|
-
|
54
|
-
|
51
|
+
m.filter do |col|
|
52
|
+
col.select { |element| element[:homepage] == homepage }
|
55
53
|
end
|
56
54
|
|
57
|
-
Now, in order to run them all on the collection, we run the
|
55
|
+
Now, in order to run them all on the collection, we run the matcher method:
|
58
56
|
|
59
57
|
@input.each do |input|
|
60
58
|
collection = @db
|
61
59
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
matcher(collection) do |m|
|
61
|
+
m.filter do |col|
|
62
|
+
col.select { |element| element[:name] == input[:name] }
|
63
|
+
end
|
64
|
+
|
65
|
+
m.filter do |col|
|
66
|
+
col.select { |element| element[:age] == input[:age]}
|
67
|
+
end
|
68
|
+
|
69
|
+
m.filter do |col|
|
70
|
+
col.select { |element| element[:homepage] == input[:homepage] }
|
71
|
+
end
|
72
|
+
|
73
|
+
m.match do |element|
|
74
|
+
element[:matched] = true
|
75
|
+
end
|
76
|
+
end
|
67
77
|
end
|
68
78
|
|
69
79
|
If any filter returns an empty result then next filter is given the collection of the last not empty result or the original data itself.
|
80
|
+
If a single result will be found the match block will be triggered. In this example we just change the entry's flag:
|
81
|
+
|
82
|
+
m.match do |element|
|
83
|
+
element[:matched] = true
|
84
|
+
end
|
70
85
|
|
71
|
-
|
86
|
+
The expected result is the db matched like:
|
72
87
|
|
73
|
-
|
74
|
-
|
75
|
-
|
88
|
+
db == [
|
89
|
+
{:id => 1, :name => "John", :age => 33, :homepage => "www.johny.com", :matched => false}
|
90
|
+
{:id => 2, :name => "Mike", :age => 30, :homepage => "www.mikes.com", :matched => false}
|
91
|
+
{:id => 3, :name => "Johny", :age => 25, :homepage => "www.johny.com", :matched => false}
|
92
|
+
{:id => 4, :name => "Mike", :age => 30, :homepage => "www.realmike.com", :matched => true}
|
93
|
+
{:id => 5, :name => "Dan", :age => 25, :homepage => "www.danny.com", :matched => true}
|
94
|
+
{:id => 6, :name => "Dan", :age => 40, :homepage => "www.fakedanny.com", :matched => false}
|
95
|
+
]
|
76
96
|
|
77
97
|
Full PeopleMatcher class example, that is responsible only for the matching is shown below:
|
78
98
|
|
@@ -85,38 +105,60 @@ Full PeopleMatcher class example, that is responsible only for the matching is s
|
|
85
105
|
@db, @input = db, input
|
86
106
|
end
|
87
107
|
|
108
|
+
#
|
109
|
+
# run filters on all data for every input element
|
110
|
+
#
|
88
111
|
def match
|
89
112
|
@input.each do |input|
|
90
113
|
collection = @db
|
91
114
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
115
|
+
matcher(collection) do |m|
|
116
|
+
m.filter do |col|
|
117
|
+
col.select { |element| element[:name] == input[:name] }
|
118
|
+
end
|
119
|
+
|
120
|
+
m.filter do |col|
|
121
|
+
col.select { |element| element[:age] == input[:age]}
|
122
|
+
end
|
123
|
+
|
124
|
+
m.filter do |col|
|
125
|
+
col.select { |element| element[:homepage] == input[:homepage] }
|
126
|
+
end
|
127
|
+
|
128
|
+
m.match do |element|
|
129
|
+
element[:matched] = true
|
130
|
+
end
|
131
|
+
end
|
97
132
|
end
|
98
133
|
end
|
134
|
+
end
|
99
135
|
|
100
|
-
|
136
|
+
Visual example
|
137
|
+
==
|
138
|
+
Filtering a collection
|
101
139
|
|
102
|
-
|
103
|
-
collection.select { |element| element[:name] == name }
|
104
|
-
end
|
140
|
+
[1,2,3] --first_filter--> [2,3] ---second_filter--> [2] -> match
|
105
141
|
|
106
|
-
|
107
|
-
collection.select { |element| element[:age] == age}
|
108
|
-
end
|
142
|
+
[1,2,3] --first_filter--> [] --> [1,2,3] --second_filter--> [3] -> match
|
109
143
|
|
110
|
-
|
111
|
-
collection.select { |element| element[:homepage] == homepage }
|
112
|
-
end
|
144
|
+
[1,2,3] --first_filter--> [1,2] --second_filter--> [1,2] --third_filter--> [1] -> match
|
113
145
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
146
|
+
[1,2,3] --first_filter--> [] --> [1,2,3] --second_filter--> [] --> [1,2,3] --third_filter--> [] -> no match
|
147
|
+
|
148
|
+
[1,2,3] --first_filter--> [1,2,3] --second_filter--> [2,3] --third_filter--> [2,3] -> no match
|
149
|
+
|
150
|
+
Filtering real data by SQL or JS
|
151
|
+
==
|
152
|
+
|
153
|
+
Remeber that often you can filter your data much more efficient with DB languages. This filtering method should be used when the filtering logic is highly application-dependent. Good SQL query (or smart map reduce) before application filter-matching is always a better approach.
|
118
154
|
|
119
155
|
Feel free to contribute or give any feedback
|
120
156
|
==
|
121
157
|
|
122
158
|
Did you find it usefull any how? Please let me know, and if you can want to improve it just send me a pull request.
|
159
|
+
|
160
|
+
Contributors
|
161
|
+
==
|
162
|
+
|
163
|
+
[Albert Llop](https://github.com/mrsimo)
|
164
|
+
[Sebastian Röbke](https://github.com/boosty)
|
@@ -1,23 +1,35 @@
|
|
1
1
|
module FilterMatcher
|
2
|
+
class Filterer
|
3
|
+
def initialize(collection)
|
4
|
+
@collection = collection
|
5
|
+
@filters = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def filter(&block)
|
9
|
+
@filters << block
|
10
|
+
end
|
11
|
+
|
12
|
+
def match(&block)
|
13
|
+
@match = block
|
14
|
+
end
|
2
15
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# - age_filter
|
8
|
-
#
|
9
|
-
# they should a filtered array
|
10
|
-
#
|
16
|
+
def process
|
17
|
+
@filters.each do |filter|
|
18
|
+
filtered = filter.call(@collection)
|
19
|
+
@collection = filtered.empty? ? @collection : filtered
|
11
20
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
collection = filtered.empty? ? collection : filtered
|
17
|
-
if collection.size == 1
|
18
|
-
element_matched(collection.first)
|
19
|
-
break
|
21
|
+
if @collection.size == 1
|
22
|
+
@match.call(@collection.first)
|
23
|
+
break
|
24
|
+
end
|
20
25
|
end
|
21
26
|
end
|
22
27
|
end
|
28
|
+
|
29
|
+
def matcher(collection)
|
30
|
+
filterer = Filterer.new(collection)
|
31
|
+
yield filterer
|
32
|
+
|
33
|
+
filterer.process
|
34
|
+
end
|
23
35
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#
|
2
|
+
# example class that uses filter matcher module
|
3
|
+
#
|
4
|
+
# @db:
|
5
|
+
# represents data from which machter tries to filter
|
6
|
+
# a single result
|
7
|
+
#
|
8
|
+
# @input:
|
9
|
+
# criteria for maching
|
10
|
+
#
|
11
|
+
|
12
|
+
require 'filter_matcher'
|
13
|
+
|
14
|
+
class PeopleMatcher
|
15
|
+
include FilterMatcher
|
16
|
+
|
17
|
+
attr_reader :db
|
18
|
+
|
19
|
+
def initialize(db, input)
|
20
|
+
@db, @input = db, input
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# run filters on all data for every input element
|
25
|
+
#
|
26
|
+
def match
|
27
|
+
@input.each do |input|
|
28
|
+
collection = @db
|
29
|
+
|
30
|
+
matcher(collection) do |m|
|
31
|
+
m.filter do |col|
|
32
|
+
col.select { |element| element[:name] == input[:name] }
|
33
|
+
end
|
34
|
+
|
35
|
+
m.filter do |col|
|
36
|
+
col.select { |element| element[:age] == input[:age]}
|
37
|
+
end
|
38
|
+
|
39
|
+
m.filter do |col|
|
40
|
+
col.select { |element| element[:homepage] == input[:homepage] }
|
41
|
+
end
|
42
|
+
|
43
|
+
m.match do |element|
|
44
|
+
element[:matched] = true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/test/people_matcher_test.rb
CHANGED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: filter_matcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0
|
5
|
+
version: 0.1.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jakub Godawa
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-07-
|
13
|
+
date: 2011-07-19 00:00:00 +02:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -35,8 +35,8 @@ files:
|
|
35
35
|
- filter_matcher.gemspec
|
36
36
|
- lib/filter_matcher.rb
|
37
37
|
- lib/filter_matcher/filter_matcher.rb
|
38
|
-
- lib/filter_matcher/people_matcher.rb
|
39
38
|
- lib/filter_matcher/version.rb
|
39
|
+
- test/people_matcher.rb
|
40
40
|
- test/people_matcher_test.rb
|
41
41
|
has_rdoc: true
|
42
42
|
homepage: http://github.com/vysogot/filter_matcher
|
@@ -67,4 +67,5 @@ signing_key:
|
|
67
67
|
specification_version: 3
|
68
68
|
summary: FilterMatcher makes it easy to find a match in a collection
|
69
69
|
test_files:
|
70
|
+
- test/people_matcher.rb
|
70
71
|
- test/people_matcher_test.rb
|
@@ -1,71 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# example class that uses filter matcher module
|
3
|
-
#
|
4
|
-
# @db:
|
5
|
-
# represents data from which machter tries to filter
|
6
|
-
# a single result
|
7
|
-
#
|
8
|
-
# @input:
|
9
|
-
# criteria for maching
|
10
|
-
#
|
11
|
-
|
12
|
-
require 'filter_matcher'
|
13
|
-
|
14
|
-
class PeopleMatcher
|
15
|
-
include FilterMatcher
|
16
|
-
|
17
|
-
attr_reader :db
|
18
|
-
|
19
|
-
def initialize(db, input)
|
20
|
-
@db, @input = db, input
|
21
|
-
end
|
22
|
-
|
23
|
-
#
|
24
|
-
# run filters on all data for every input element
|
25
|
-
#
|
26
|
-
def match
|
27
|
-
@input.each do |input|
|
28
|
-
collection = @db
|
29
|
-
|
30
|
-
#
|
31
|
-
# filters are run sequentially until they find
|
32
|
-
# a single result
|
33
|
-
#
|
34
|
-
# they are called by the following convention:
|
35
|
-
# :by_<what_it_filters => paramters of <what_it_filters>_filter method
|
36
|
-
#
|
37
|
-
# when one of the filters reveal a single result
|
38
|
-
# next filters are not run for current input
|
39
|
-
#
|
40
|
-
filter_and_match collection, {
|
41
|
-
:by_name => input[:name],
|
42
|
-
:by_age => input[:age],
|
43
|
-
:by_homepage => input[:homepage]
|
44
|
-
}
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
#
|
51
|
-
# filters are named by the following convention:
|
52
|
-
# <what_it_filters>_filter
|
53
|
-
#
|
54
|
-
# every filter should return a filtered result
|
55
|
-
#
|
56
|
-
def name_filter(collection, name)
|
57
|
-
collection.select { |element| element[:name] == name }
|
58
|
-
end
|
59
|
-
|
60
|
-
def age_filter(collection, age)
|
61
|
-
collection.select { |element| element[:age] == age}
|
62
|
-
end
|
63
|
-
|
64
|
-
def homepage_filter(collection, homepage)
|
65
|
-
collection.select { |element| element[:homepage] == homepage }
|
66
|
-
end
|
67
|
-
|
68
|
-
def element_matched(element)
|
69
|
-
element[:matched] = true
|
70
|
-
end
|
71
|
-
end
|