autocompletion 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ README
2
+ ======
3
+
4
+
5
+ Summary
6
+ -------
7
+ This gem provides fast prefix-autocompletion in pure ruby.
8
+
9
+
10
+ Installation
11
+ ------------
12
+ `gem install autocompletion`
13
+
14
+
15
+ Usage
16
+ -----
17
+
18
+ ### Autocomplete words
19
+ auto = AutoCompletion.words(%w[foo bar baz])
20
+ auto.complete('f') # => ["foo"]
21
+ auto.complete('b') # => ["bar", "baz"]
22
+ auto.complete('z') # => []
23
+
24
+ ### Autocomplete objects by attributes
25
+ Person = Struct.new(:first_name, :last_name)
26
+ people = [
27
+ Person.new("Peter", "Parker"),
28
+ Person.new("Luke", "Skywalker"),
29
+ Person.new("Anakin", "Skywalker"),
30
+ ]
31
+ auto = AutoCompletion.map(people) { |person|
32
+ [person.first_name, person.last_name]
33
+ }
34
+
35
+ auto.complete("P")
36
+ # => [#<struct Person first_name="Peter", last_name="Parker">]
37
+
38
+ auto.complete("S")
39
+ # => [#<struct Person first_name="Luke", last_name="Skywalker">,
40
+ # #<struct Person first_name="Anakin", last_name="Skywalker">]
41
+
42
+ auto.complete("S", "L")
43
+ # => [#<struct Person first_name="Luke", last_name="Skywalker">]
44
+
45
+
46
+ Links
47
+ -----
48
+
49
+ * __Github__ http://github.com/apeiros/autocompletion
50
+ * __Documentation__ http://rdoc.info/github/apeiros/autocompletion/master/frames
51
+ * __Rubygems__ http://rubygems.org/gems/autocompletion
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../rake/lib', __FILE__))
2
+ Dir.glob(File.expand_path('../rake/tasks/**/*.{rake,task,rb}', __FILE__)) do |task_file|
3
+ begin
4
+ import task_file
5
+ rescue LoadError => e
6
+ warn "Failed to load task file #{task_file}"
7
+ warn " #{e.class} #{e.message}"
8
+ warn " #{e.backtrace.first}"
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "autocompletion"
5
+ s.version = "0.0.1"
6
+ s.authors = "Stefan Rusterholz"
7
+ s.email = "stefan.rusterholz@gmail.com"
8
+ s.homepage = "http://github.com/apeiros/autocompletion"
9
+
10
+ s.description = <<-DESCRIPTION.gsub(/^ /, '').chomp
11
+ This gem provides fast prefix-autocompletion in pure ruby.
12
+ DESCRIPTION
13
+
14
+ s.summary = <<-SUMMARY.gsub(/^ /, '').chomp
15
+ Fast prefix-autocompletion in pure ruby.
16
+ SUMMARY
17
+
18
+ s.files =
19
+ Dir['bin/**/*'] +
20
+ Dir['lib/**/*'] +
21
+ Dir['rake/**/*'] +
22
+ Dir['test/**/*'] +
23
+ %w[
24
+ autocompletion.gemspec
25
+ Rakefile
26
+ README.markdown
27
+ ]
28
+
29
+ if File.directory?('bin') then
30
+ executables = Dir.chdir('bin') { Dir.glob('**/*').select { |f| File.executable?(f) } }
31
+ s.executables = executables unless executables.empty?
32
+ end
33
+
34
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1")
35
+ s.rubygems_version = "1.3.1"
36
+ s.specification_version = 3
37
+ end
@@ -0,0 +1,204 @@
1
+ # encoding: utf-8
2
+
3
+
4
+
5
+ require 'autocompletion/version'
6
+
7
+
8
+
9
+ # AutoCompletion
10
+ # A binary search for prefix-matching is used to determine left- and right-
11
+ # boundary. This means even with 1_000_000 items, a maximum of 40 comparisons
12
+ # is required.
13
+ #
14
+ # @example Autocomplete words
15
+ # auto = AutoCompletion.words(%w[foo bar baz])
16
+ # auto.complete('f') # => ["foo"]
17
+ # auto.complete('b') # => ["bar", "baz"]
18
+ # auto.complete('z') # => []
19
+ #
20
+ # @example Autocomplete objects by attributes
21
+ # Person = Struct.new(:first_name, :last_name)
22
+ # people = [
23
+ # Person.new("Peter", "Parker"),
24
+ # Person.new("Luke", "Skywalker"),
25
+ # Person.new("Anakin", "Skywalker"),
26
+ # ]
27
+ # auto = AutoCompletion.map(people) { |person|
28
+ # [person.first_name, person.last_name]
29
+ # }
30
+ #
31
+ # auto.complete("P")
32
+ # # => [#<struct Person first_name="Peter", last_name="Parker">]
33
+ #
34
+ # auto.complete("S")
35
+ # # => [#<struct Person first_name="Luke", last_name="Skywalker">,
36
+ # # #<struct Person first_name="Anakin", last_name="Skywalker">]
37
+ #
38
+ # auto.complete("S", "L")
39
+ # # => [#<struct Person first_name="Luke", last_name="Skywalker">]
40
+ class AutoCompletion
41
+
42
+ # Raised by AutoCompletion::new
43
+ class InvalidOrder < ArgumentError
44
+ def initialize
45
+ super("The prefixes are not in sorted order")
46
+ end
47
+ end
48
+
49
+ # @return [AutoCompletion]
50
+ # An autocompletion for a list of words.
51
+ def self.words(words)
52
+ unordered_tuples(words.map { |word| [word, word] })
53
+ end
54
+
55
+ # @return [AutoCompletion]
56
+ # Map a list of entities to their keys. The block should return an array of valid
57
+ # prefixes for the yielded entity.
58
+ def self.map(entities)
59
+ mapped = entities.flat_map { |entity|
60
+ keys = yield(entity)
61
+ keys.flat_map { |key| [key, entity] }
62
+ }
63
+
64
+ unordered_tuples(mapped.each_slice(2))
65
+ end
66
+
67
+ # @return [AutoCompletion]
68
+ # Creates an AutoCompletion for an unordered array of the form [["prefix", value], …].
69
+ def self.unordered_tuples(entities)
70
+ new(entities.sort_by(&:first).flatten(1), true)
71
+ end
72
+
73
+ # All stored entities. A flat array of the form [prefix1, value1, prefix2, value2, …]
74
+ attr_reader :entities
75
+
76
+ # @param [Array<Array>] entities
77
+ # A flat array of the form [prefix1, value1, prefix2, value2, …] containing all
78
+ # prefixes and their corresponding value.
79
+ # @param [Boolean] force
80
+ # If force is set to true, the order of entities is not verified. Use this only if
81
+ # you know what you're doing.
82
+ #
83
+ # @see AutoCompletion::words, AutoCompletion::map
84
+ def initialize(entities, force=false)
85
+ @entities = entities
86
+ raise InvalidOrder.new unless force || valid?
87
+ end
88
+
89
+ # @return [Boolean]
90
+ # Returns true if the prefixes are in a valid order.
91
+ def valid?
92
+ @entities.each_slice(2).each_cons(2) do |(a,_),(b,_)|
93
+ return false unless a <= b
94
+ end
95
+ true
96
+ end
97
+
98
+ # @return [Boolean]
99
+ # Returns true if there are no prefixes stored in this AutoCompletion instance.
100
+ def empty?
101
+ @entities.empty?
102
+ end
103
+
104
+ # @return [Integer]
105
+ # The number of prefixes stored. Note that the same prefix can occur multiple times.
106
+ def size
107
+ @entities.size>>1
108
+ end
109
+
110
+ # @return [Integer] The number of distinct entities
111
+ def count_distinct_entitites
112
+ result = {}
113
+ @entities.each_slice(2) do |key, value|
114
+ result[value] = true
115
+ end
116
+
117
+ result.size
118
+ end
119
+
120
+ # @return [Integer] The number of distinct prefixes
121
+ def count_distinct_prefixes
122
+ result = {}
123
+ @entities.each_slice(2) do |key, value|
124
+ result[key] = true
125
+ end
126
+
127
+ result.size
128
+ end
129
+
130
+ # @param [String] prefixes
131
+ # A list of prefixes to match. All given prefixes must be matched.
132
+ #
133
+ # @return [Array]
134
+ # Returns an array of distinct entities matching the given prefixes.
135
+ def complete(*prefixes)
136
+ # short-cut
137
+ return [] if empty? || prefixes.any? { |word|
138
+ word < @entities.first[0,word.size] || word > @entities[-2][0,word.size]
139
+ }
140
+
141
+ slices = prefixes.map { |word| range_search(word) }
142
+ return [] if slices.include?(nil) # short-cut
143
+
144
+ result = @entities[slices.pop].each_slice(2).map(&:last).uniq
145
+ slices.each do |slice|
146
+ result &= @entities[slice].each_slice(2).map(&:last)
147
+ end
148
+
149
+ result
150
+ end
151
+
152
+ # @return [nil, Range<Integer>]
153
+ # Returns nil if AutoCompletion#empty?
154
+ # Returns -1..-1 if the prefix is smaller than the smallest key
155
+ # Returns AutoCompletion#size..AutoCompletion#size if the prefix is bigger than the biggest key
156
+ # Returns the range for all matched keys and values otherwise
157
+ def range_search(prefix)
158
+ prefix_size = prefix.size
159
+ length = size()
160
+ found = nil
161
+ left = 0
162
+ right = length-1
163
+ found = false
164
+ max_exc_right = length
165
+
166
+ return nil if empty?
167
+ return -1..-1 if @entities[0][0,prefix_size] > prefix # prefix is smaller than smallest value
168
+ return length..length if @entities[-2][0,prefix_size] < prefix # prefix is bigger than biggest value
169
+
170
+ # binary search for smallest index
171
+ # mark biggest right that include prefix, and biggest mark that doesn't include prefix
172
+ while(left<right)
173
+ index = (left+right)>>1
174
+ cmp_value = @entities.at(index<<1)[0,prefix_size]
175
+ case cmp_value <=> prefix
176
+ when -1 then
177
+ left = index+1
178
+ when 1 then
179
+ right = index
180
+ max_exc_right = right
181
+ else # 0
182
+ right = index
183
+ end
184
+ end
185
+ return nil unless @entities.at(left<<1)[0,prefix_size] == prefix
186
+ final_left = left
187
+
188
+ # binary search for biggest index
189
+ right = max_exc_right-1
190
+ while(left<right)
191
+ index = (left+right)>>1
192
+ cmp_value = @entities.at(index<<1)[0,prefix_size]
193
+ if cmp_value > prefix then
194
+ right = index
195
+ else
196
+ left = index+1
197
+ end
198
+ end
199
+ final_right = right
200
+ final_right -= 1 unless @entities.at(right<<1)[0,prefix_size] == prefix
201
+
202
+ return (final_left<<1)..((final_right<<1)+1)
203
+ end
204
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require 'rubygems/version' # newer rubygems use this
5
+ rescue LoadError
6
+ require 'gem/version' # older rubygems use this
7
+ end
8
+
9
+ class AutoCompletion
10
+
11
+ # The version of the gem
12
+ Version = Gem::Version.new("0.0.1")
13
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: autocompletion
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Stefan Rusterholz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-19 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: This gem provides fast prefix-autocompletion in pure ruby.
15
+ email: stefan.rusterholz@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/autocompletion/version.rb
21
+ - lib/autocompletion.rb
22
+ - autocompletion.gemspec
23
+ - Rakefile
24
+ - README.markdown
25
+ homepage: http://github.com/apeiros/autocompletion
26
+ licenses: []
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ! '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>'
41
+ - !ruby/object:Gem::Version
42
+ version: 1.3.1
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 1.8.15
46
+ signing_key:
47
+ specification_version: 3
48
+ summary: Fast prefix-autocompletion in pure ruby.
49
+ test_files: []