collection_utils 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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