collection_utils 1.0.0 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 917412941f37688077b9cfb8204edd619dc9f9e3
4
- data.tar.gz: 0f630cb9f5bb5cdbfd1d0e58d045dd8b6df6e563
3
+ metadata.gz: be93bf3ee75c35ec31ddec4deaf498b9e6b64787
4
+ data.tar.gz: 3bf11d6eb814dc595f9ff929939dbae2daad110b
5
5
  SHA512:
6
- metadata.gz: 971de239372c801638b03a34d0c7fdc7e1894cebbd212848f6ff4ba598cd1d0757173c9c18f4bff9d10ebf2b0d83647503bc9b639b5e4e53a31523307437250e
7
- data.tar.gz: a8b94362e213e51afb85a4531b9d073a8586a9f27bf0e2b9b7a7d1169e9cbfd85a4f6056c0b41aa9edf976b99e93e1dfb330f5c036ca884eb3b6938b59635356
6
+ metadata.gz: 50cf2ca18e195ad0dddb8df31b1f88919fc4a6b98a576ae2b83053e5fba91596aed59f8a82108a161ca1cbcc039e6a449f36948b4825c283147f527e98487ab2
7
+ data.tar.gz: 550aeb6cc93cfd1246f22f44fe946f1c79798072f289c71cb7ab11cc1635ebd1463db002db8b789c5c939511edd6a82edb15b53f6ce076da11a6aa1865fe80f6
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/Gemfile CHANGED
@@ -2,3 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in collection_utils.gemspec
4
4
  gemspec
5
+
6
+ gem 'coveralls', require: false
7
+ gem 'simplecov', :require => false, :group => :test
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # CollectionUtils [![Build Status](https://travis-ci.org/deeshugupta/collection_utils.svg?branch=master)](https://travis-ci.org/deeshugupta/collection_utils)
1
+ # CollectionUtils [![Build Status](https://travis-ci.org/deeshugupta/collection_utils.svg?branch=master)](https://travis-ci.org/deeshugupta/collection_utils) [![Coverage Status](https://coveralls.io/repos/github/deeshugupta/collection_utils/badge.svg?branch=master)](https://coveralls.io/github/deeshugupta/collection_utils?branch=master) [![Gem Version](https://badge.fury.io/rb/collection_utils.svg)](https://badge.fury.io/rb/collection_utils)
2
2
 
3
3
  CollectionUtils provide with basic collection templates like stack, queues and heaps e.t.c for easier development.
4
4
 
@@ -1,4 +1,4 @@
1
1
  Dir[File.dirname(__FILE__) + '/collection_utils/*.rb'].each {|file| require file }
2
2
  module CollectionUtils
3
- # Your code goes here...
3
+
4
4
  end
@@ -0,0 +1,114 @@
1
+ module CollectionUtils
2
+ class HashDeserializedObject
3
+ private
4
+ attr_accessor :original
5
+
6
+ def convert_name(name)
7
+ attr_name = name.gsub(/([A-Z])([A-Z][a-z])/, '\1_\2')
8
+ attr_name.gsub!(/([a-z])([A-Z])/, '\1_\2')
9
+ return attr_name.to_s.downcase.gsub(/[\s:+!\?\.\|\{\}\[\]\+\-\*\^%$#@]/, "_")
10
+ end
11
+
12
+ def define_new_method(name, &block)
13
+ (class << self; self; end).class_eval do
14
+ define_method name, &block
15
+ end
16
+ end
17
+
18
+ public
19
+
20
+ def initialize(hash = {})
21
+ @original = hash
22
+ hash.each do |key, value|
23
+ insert(key, value)
24
+ end
25
+ end
26
+
27
+ # Add arguments on the runtime. Define attr_accessor for the arguments
28
+ # and get original hash for the same. Delete the unecessary arguments using
29
+ # delete method
30
+ # @param [Symbol] argument_name name of the argument that needs to be added
31
+ # @param *args value of the argument
32
+ # @example Add argument on runtime
33
+ # => var = CollectionUtils::HashDeserializedObject.new
34
+ # => var.query.bool.must.match = "hello"
35
+ # => var.get_serialized_object #{:query=>{:bool=>{:must=>{"match"=>"hello"}}}}
36
+ def method_missing(argument_name, *args)
37
+ if argument_name.to_s == "original"
38
+ super
39
+ return
40
+ end
41
+ name = argument_name
42
+ if name.to_s.include?"="
43
+ name = name.to_s.split("=").first
44
+ insert(name, args.first)
45
+ else
46
+ insert(name, {})
47
+ end
48
+ return instance_variable_get("@#{name}")
49
+ end
50
+
51
+ # Insert new key value pair in deserialized object.
52
+ # This will create an attr_accessor with key and value as given value
53
+ # @param name key of the hash
54
+ # @param value value of the given jey
55
+ # @example Insert an value in deserialized object and access it
56
+ # => obj = CollectionUtils::HashDeserializedObject.new()
57
+ # => obj.insert(:name, "CollectionUtils")
58
+ # => obj.name # CollectionUtils
59
+ def insert(name, value)
60
+ @original[name] = value
61
+ if value.class.name == "Hash" || value.class.superclass.name == "Hash"
62
+ value = CollectionUtils::HashDeserializedObject.new(value)
63
+ end
64
+ name = convert_name(name.to_s)
65
+ self.class.send(:attr_accessor, name)
66
+ instance_variable_set("@#{name}", value)
67
+ define_singleton_method "#{name}=".to_sym do |arg|
68
+ @original[name] = arg
69
+ instance_variable_set("@#{name}", arg)
70
+ end
71
+ define_singleton_method "#{name}".to_sym do
72
+ instance_variable_get("@#{name}")
73
+ end
74
+ end
75
+
76
+
77
+ # Delete the key value pair from deserialized object.
78
+ # This will rmeove the attr_accessor from object and key value from hash.
79
+ # @param name attr_accessor name to be deleted
80
+ # @return value of the deleted attribute
81
+ # @example Delete from object
82
+ # => obj = CollectionUtils::HashDeserializedObject.new()
83
+ # => obj.insert(:name, "CollectionUtils")
84
+ # => obj.name #CollectionUtils
85
+ # => obj.insert(:type, "HashDeserializedObject")
86
+ # => obj.type #HashDeserializedObject
87
+ # => value = obj.delete(:name) #CollectionUtils
88
+ # => obj.get_serialized_object #{type: "HashDeserializedObject"}
89
+ def delete(name)
90
+ @original.delete(name)
91
+ instance_eval("undef :#{name.to_s}")
92
+ instance_eval("undef :#{name.to_s}=")
93
+ return remove_instance_variable("@#{name.to_s}")
94
+ end
95
+
96
+ # Get original Hash used to build the object. It will grow as we insert more
97
+ # values in the object
98
+ # @return [Hash] original hash used to build the object
99
+ # @example Create Object from hash, change the hash and get original
100
+ # => hash = {:message1 => "welcome", :message2 => "to" }
101
+ # => hash_new = CollectionUtils::HashDeserializedObject.new(hash)
102
+ # => hash_new(:message3, "Collections")
103
+ # => hash = hash_new.get_serialized_object # hash = {:message1 => "welcome", :message2 => "to", :message3 => "collections"}
104
+ def get_serialized_object
105
+ return @original
106
+ end
107
+ end
108
+ end
109
+
110
+ class Hash
111
+ def to_hash_deserialized_object
112
+ return CollectionUtils::HashDeserializedObject.new(self)
113
+ end
114
+ end
@@ -1,28 +1,46 @@
1
1
  module CollectionUtils
2
2
  class Heap
3
3
  private
4
- attr_accessor :heap
4
+ attr_accessor :root, :incomplete_set, :leaf_set, :size, :level
5
5
 
6
- protected
6
+ class Node
7
+ attr_accessor :val, :left, :right, :parent, :level
7
8
 
9
+ def initialize(val, left=nil, right=nil, parent=nil, level=nil)
10
+ @val = val
11
+ @left = left
12
+ @right = right
13
+ @parent = parent
14
+ @level = level
15
+ end
16
+
17
+ def is_leaf?
18
+ left.nil? && right.nil?
19
+ end
20
+
21
+ def is_full?
22
+ !left.nil? && !right.nil?
23
+ end
24
+
25
+ def is_incomplete?
26
+ (!left.nil? && right.nil?) || (left.nil? && !right.nil?)
27
+ end
8
28
 
9
- #
10
- # @return element which is at the root of the tree
11
- def root
12
- return @heap.first
13
29
  end
14
30
 
31
+ private_constant :Node
15
32
 
33
+ protected
16
34
  # Traverse the tree in pre-order manner
17
35
  #
18
36
  # @param node current node being parsed
19
37
  # @param [Array] arr which will contain the nodes of tree in pre-order manner
20
38
  def pre_order(node, arr)
21
39
  return if node.nil?
22
- left = left(node[:index])
23
- right = right(node[:index])
40
+ left = node.left
41
+ right = node.right
24
42
 
25
- arr << node[:element]
43
+ arr << node.val
26
44
  pre_order(left, arr) unless left.nil?
27
45
  pre_order(right, arr) unless right.nil?
28
46
 
@@ -36,12 +54,12 @@ module CollectionUtils
36
54
  # @param [Array] arr which will contain the nodes of tree in post-order manner
37
55
  def post_order(node, arr)
38
56
  return if node.nil?
39
- left = left(node[:index])
40
- right = right(node[:index])
57
+ left = node.left
58
+ right = node.right
41
59
 
42
60
  post_order(left, arr) unless left.nil?
43
61
  post_order(right, arr) unless right.nil?
44
- arr << node[:element]
62
+ arr << node.val
45
63
  return
46
64
  end
47
65
 
@@ -52,118 +70,157 @@ module CollectionUtils
52
70
  # @param [Array] arr which will contain the nodes of tree in in-order manner
53
71
  def in_order(node, arr)
54
72
  return if node.nil?
55
- left = left(node[:index])
56
- right = right(node[:index])
73
+ left = node.left
74
+ right = node.right
57
75
 
58
76
  in_order(left, arr) unless left.nil?
59
- arr << node[:element]
77
+ arr << node.val
60
78
  in_order(right, arr) unless right.nil?
61
79
  return
62
80
  end
63
81
 
64
82
 
65
- # @param [Integer] parent is the index for which left child needs to be founded. Default is 0
66
- # @return left_child and index of that left child
67
- def left_child(parent = 0)
68
- left = (2*parent + 1)
69
- return nil, nil if @heap[left].nil?
70
- return @heap[left], left
71
- end
72
-
73
-
74
- # @param [Integer] parent is the index for which right child needs to be founded. Default is 0
75
- # @return right_child and index of that right child
76
- def right_child(parent = 0)
77
- right = (2*parent + 2)
78
- return nil, nil if @heap[right].nil?
79
- return @heap[right], right
80
- end
81
-
82
-
83
- # @param parent is the index for which left child is added. Default is 0
84
- # @param node is the element which needs to be assigned to left of the parent
85
- def assign_left(parent = 0, node)
86
- left = (2*parent + 1)
87
- node[:index] = left
88
- @heap[left] = node
89
- end
90
-
91
-
92
- # @param parent is the index for which right child is added. Default is 0
93
- # @param node is the element which needs to be assigned to right of the parent
94
- def assign_right(parent = 0, node)
95
- right = (2*parent + 2)
96
- node[:index] = right
97
- @heap[right] = node
98
- end
99
-
100
- # @param parent for which left child index needs to be found
101
- # @return [Integer] left child index
102
- def left_index(parent = 0)
103
- left = (2*parent + 1)
104
- end
105
-
106
- # @param parent for which right child index needs to be found
107
- # @return [Integer] right child index
108
- def right_index(parent = 0)
109
- right = (2*parent + 2)
110
- end
111
-
112
- # @param child for which parent needs to be found
113
- # @return element and parent index
114
- def parent(child = 0)
115
- par = child/2
116
- return @heap[par], par
117
- end
118
-
119
-
120
83
  public
121
84
  #Constructors
122
85
  def initialize(array = [])
123
- @heap = []
86
+ @size = 0
87
+ @root = nil
88
+ @incomplete_set = CollectionUtils::Set.new()
89
+ @leaf_set = CollectionUtils::Set.new()
124
90
  array.each do |element|
125
- push(element)
91
+ insert(element)
126
92
  end
127
93
  end
128
94
 
129
- #Public methods
95
+ # Adds an element to the heap. This is done in O(1) operations and
96
+ # preference is given to incomplete nodes as compared to leaf nodes
97
+ # @param element object that needs to be added to heap
98
+ # => @heap = CollectionUtils::Heap.new([5,2,6,4,3])
99
+ # => # 5
100
+ # => # / \
101
+ # => # 2 6
102
+ # => # / \
103
+ # => # 4 3
104
+ # => @heap.insert(7)
105
+ # => # 5
106
+ # => # / \
107
+ # => # 2 6
108
+ # => # / \ /
109
+ # => # 4 3 7
110
+ def insert(element)
111
+ node = Node.new(element)
112
+ @size += 1
113
+ if @root.nil?
114
+ @root = node
115
+ @root.level = 1
116
+ @level = 1
117
+ @leaf_set.insert(node)
118
+ return
119
+ end
120
+ unless @incomplete_set.is_empty?
121
+ parent_node = @incomplete_set.get
122
+ @incomplete_set.delete(parent_node)
123
+ if parent_node.left.nil?
124
+ node.parent = parent_node
125
+ node.level = parent_node.level + 1
126
+ parent_node.left = node
127
+ @level = node.level if node.level > @level
128
+ @incomplete_set.insert(parent_node) if parent_node.right.nil?
129
+ @leaf_set.insert(node)
130
+ return
131
+ end
132
+ if parent_node.right.nil?
133
+ node.parent = parent_node
134
+ node.level = parent_node.level + 1
135
+ parent_node.right = node
136
+ @level = node.level if node.level > @level
137
+ @incomplete_set.insert(parent_node) if parent_node.left.nil?
138
+ @leaf_set.insert(node)
139
+ return
140
+ end
141
+ end
130
142
 
131
- # @param [Integer] parent index for which left child needs to be returned
132
- # @return left child
133
- def left(parent = 0)
134
- left_child(parent).first
135
- end
143
+ unless @leaf_set.is_empty?
144
+ parent_node = @leaf_set.get
145
+ @leaf_set.delete(parent_node)
146
+ node.parent = parent_node
147
+ node.level = parent_node.level + 1
148
+ parent_node.left = node
149
+ @level = node.level if node.level > @level
150
+ @incomplete_set.insert(parent_node)
151
+ @leaf_set.insert(node)
152
+ return
153
+ end
136
154
 
137
- # @param [Integer] parent index for which right child needs to be returned
138
- # @return right child
139
- def right(parent = 0)
140
- right_child(parent).first
141
155
  end
142
156
 
143
- # push element to heap
144
- #
145
- # @param element object that needs to be added to heap
146
- def push(element)
147
- @heap << {element: element, index: size}
157
+ # Removes a random element from the leaf set and
158
+ # deletes it. This is done in O(1) operations.
159
+ # @return removed element
160
+ # @example delete from heap [5,2,6,4,3]
161
+ # => @heap = CollectionUtils::Heap.new([5,2,6,4,3])
162
+ # => # 5
163
+ # => # / \
164
+ # => # 2 6
165
+ # => # / \
166
+ # => # 4 3
167
+ # => @heap.delete == 4 or 3 or 6
168
+ def delete
169
+ @size -= 1
170
+ node = @leaf_set.get
171
+ @leaf_set.delete(node)
172
+ parent = node.parent
173
+ value = node.val
174
+ if parent.is_incomplete?
175
+ @incomplete_set.delete(parent)
176
+ parent.left == node ? parent.left = nil : parent.right = nil
177
+ node = nil
178
+ @leaf_set.insert(parent) if parent.is_leaf?
179
+ else
180
+ parent.left == node ? parent.left = nil : parent.right = nil
181
+ node = nil
182
+ @incomplete_set.insert(parent)
183
+ end
184
+ return value
148
185
  end
149
186
 
150
- # pop an element from heap
187
+ # Returns the root of the tree
151
188
  #
152
- # @return removed element
153
- def pop
154
- element = @heap.pop
155
- return element[:element]
189
+ # @return element which is present at the root of the heap or tree
190
+ # @example Root of heap [5,2,6,4,3]
191
+ # => @heap = CollectionUtils::Heap.new([5,2,6,4,3])
192
+ # => # 5
193
+ # => # / \
194
+ # => # 2 6
195
+ # => # / \
196
+ # => # 4 3
197
+ # => @heap.root == 5
198
+ def root
199
+ @root.val
156
200
  end
157
201
 
158
-
202
+ # Returns the number of elements in a tree or heap. This is returned in
203
+ # O(1) operations as we are storing the size of the tree which is
204
+ # otherwise O(n) operations.
159
205
  # @return [Integer] size of heap
160
206
  def size
161
- return @heap.size
207
+ @size
162
208
  end
163
209
 
210
+ # Returns whether the heap or tree is empty. This is just a syntax_sugar
211
+ # for size == 0
164
212
  # @return [Boolean] heap's emptiness
165
213
  def is_empty?
166
- return size == 0
214
+ size == 0
215
+ end
216
+
217
+ # Returns the level of the tree. This is returned in O(1) operations
218
+ # as we are storing the level of the tree which is otherwise a costly
219
+ # operation
220
+ #
221
+ # @return [Integer] height or level of the heap or tree
222
+ def level
223
+ @level
167
224
  end
168
225
 
169
226
 
@@ -173,19 +230,19 @@ module CollectionUtils
173
230
  # arr = [1,2,3,4,5]
174
231
  # heap = CollectionUtils::Heap.new(arr)
175
232
  # x = []
176
- # @heap.bfs do |element|
233
+ # heap.bfs do |element|
177
234
  # x << element
178
235
  # end
179
236
  # #x = [1,2,3,4,5]
180
237
  def bfs
181
238
  queue = CollectionUtils::Queue.new
182
- queue.enqueue(root)
239
+ queue.enqueue(@root)
183
240
  while true do
184
241
  node = queue.dequeue
185
242
  next if node.nil?
186
- left = left(node[:index])
187
- right = right(node[:index])
188
- yield(node[:element]) if block_given?
243
+ left = node.left
244
+ right = node.right
245
+ yield(node.val) if block_given?
189
246
  queue.enqueue(left) unless left.nil?
190
247
  queue.enqueue(right) unless right.nil?
191
248
  break if queue.is_empty?
@@ -199,19 +256,19 @@ module CollectionUtils
199
256
  # arr = [1,2,3,4,5]
200
257
  # heap = CollectionUtils::Heap.new(arr)
201
258
  # x = []
202
- # @heap.dfs do |element|
259
+ # heap.dfs do |element|
203
260
  # x << element
204
261
  # end
205
- # #x = [1,3,2,5,4]
262
+ # #x = [1,3,5,4,2]
206
263
  def dfs
207
264
  stack = CollectionUtils::Stack.new
208
- stack.push(root)
265
+ stack.push(@root)
209
266
  while true do
210
267
  node = stack.pop
211
268
  next if node.nil?
212
- left = left(node[:index])
213
- right = right(node[:index])
214
- yield(node[:element]) if block_given?
269
+ left = node.left
270
+ right =node.right
271
+ yield(node.val) if block_given?
215
272
  stack.push(left) unless left.nil?
216
273
  stack.push(right) unless right.nil?
217
274
  break if stack.is_empty?
@@ -221,27 +278,51 @@ module CollectionUtils
221
278
  # Pre-Order Traversal of Tree
222
279
  #
223
280
  # @return [Array] elements of heap in pre-ordered manner
281
+ # @example Preorder Traversal for heap [5,2,6,4,3]
282
+ # => @heap = CollectionUtils::Heap.new([5,2,6,4,3])
283
+ # => # 5
284
+ # => # / \
285
+ # => # 2 6
286
+ # => # / \
287
+ # => # 4 3
288
+ # => arr = @heap.preorder #arr = [5,2,4,3,6]
224
289
  def preorder
225
290
  arr = []
226
- pre_order(root, arr)
291
+ pre_order(@root, arr)
227
292
  return arr
228
293
  end
229
294
 
230
295
  # Post-Order Traversal of Tree
231
296
  #
232
297
  # @return [Array] elements of heap in post-ordered manner
298
+ # @example Postorder Traversal for heap [5,2,6,4,3]
299
+ # => @heap = CollectionUtils::Heap.new([5,2,6,4,3])
300
+ # => # 5
301
+ # => # / \
302
+ # => # 2 6
303
+ # => # / \
304
+ # => # 4 3
305
+ # => arr = @heap.postorder #arr = [4,3,2,6,5]
233
306
  def postorder
234
307
  arr = []
235
- post_order(root, arr)
308
+ post_order(@root, arr)
236
309
  return arr
237
310
  end
238
311
 
239
312
  # In-Order Traversal of Tree
240
313
  #
241
314
  # @return [Array] elements of heap in in-ordered manner
315
+ # @example Inorder Traversal for heap [5,2,6,4,3]
316
+ # => @heap = CollectionUtils::Heap.new([5,2,6,4,3])
317
+ # => # 5
318
+ # => # / \
319
+ # => # 2 6
320
+ # => # / \
321
+ # => # 4 3
322
+ # => arr = @heap.inorder #arr = [4,2,3,5,6]
242
323
  def inorder
243
324
  arr = []
244
- in_order(root, arr)
325
+ in_order(@root, arr)
245
326
  return arr
246
327
  end
247
328