twins 0.0.2 → 0.0.3

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/twins.rb +99 -14
  3. data/lib/twins/version.rb +1 -1
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 20e9e6421c19c6abccbb7f121b1ba3a36ae56a7e
4
- data.tar.gz: 660fdd54b8e6acb0d2a947c329e0293d147919af
3
+ metadata.gz: 330a31cfe1d6f44addbd04f07ef8ce599c1e7ab5
4
+ data.tar.gz: c34f97c80fc1c360fbbb4c9a6ba4e0b3b34ef180
5
5
  SHA512:
6
- metadata.gz: e2dababf31cb5d844ae4ff9588758e27538fad29fd086354a6f43e66640647f0c8339c838a17c34515339bd6191286ea6618e4fd042a82bee70140980c905710
7
- data.tar.gz: fd4442cd82dbc5d41df4d605d724c03c3070e8e363dd4b0b97b54454e298b059289de248e1c6284b6109efdb1812f580f2977af6bb48a00dbb1da2c08b05aefc
6
+ metadata.gz: 7a3eec8cb57b212ca23e94b40a26fd077f0552ce0918691dd51994e45b0bf0bcf5d154c0a28c38a8815959db19e8388cd53625942fa488d6aa08960d69303422
7
+ data.tar.gz: f6c9119803bdb1661d57433088b3f6454d14da1720428cf94752573efd61ec8da020e0b2d1d7efd362bd127394af3f3de076a891c244c7e4b88becc6bf35c1cd
@@ -3,34 +3,32 @@ require 'twins/utilities'
3
3
 
4
4
  module Twins
5
5
 
6
- # @param collection [Enumerable] A collection of Hash objects
6
+ # Consolidates keys with mode or lowest distance
7
+ #
8
+ # @param collection [Enumerable] A collection of Hash or Hash-like objects
7
9
  # @param options [Hash]
8
- # @return [Hash, Nil]
10
+ # @return [HashWithIndifferentAccess, Nil]
9
11
  def consolidate(collection, options = {})
10
12
  return nil unless collection.any?
13
+ ensure_collection_uniformity!(collection)
11
14
 
12
- if collection.all? { |e| e.is_a?(Hash) }
13
- # noop
14
- elsif collection.all? { |e| e.is_a?(collection.first.class) }
15
- collection = collection.map do |element|
16
- Hash[element.instance_variables.map { |name| [name.to_s.sub(/\A@/, ''), element.instance_variable_get(name)] }]
17
- end
15
+ if collection.first.is_a?(Hash)
16
+ indiff_collection = collection
18
17
  else
19
- raise ArgumentError, "The collection's elements must all be of the same Class"
18
+ indiff_collection = collection.map { |element| element.to_hash }
20
19
  end
21
20
 
22
21
  options = options.with_indifferent_access
23
22
  consolidated = Hash.new
24
23
 
25
- collection.each do |hash|
26
- hash.each_pair do |key, value|
27
-
24
+ indiff_collection.each do |element|
25
+ element.each_pair do |key, value|
28
26
  # Recursively consolidate nested hashes
29
27
  if value.is_a?(Hash) && !consolidated[key]
30
- consolidated[key] = consolidate(collection.map { |element| element[key] })
28
+ consolidated[key] = consolidate(indiff_collection.map { |el| el[key] })
31
29
  else
32
30
  # Filter elements without a given key to avoid unintentionally nil values
33
- values = collection.select { |element| element.has_key?(key) }.map { |element| element[key] }
31
+ values = indiff_collection.select { |el| el.has_key?(key) }.map { |el| el[key] }
34
32
 
35
33
  if options[:priority].try(:[], key)
36
34
  # Compute each element's distance from the given priority
@@ -49,4 +47,91 @@ module Twins
49
47
  consolidated.with_indifferent_access
50
48
  end
51
49
  module_function :consolidate
50
+
51
+ # Find element with the highest count of modes or the lowest overall distances
52
+ #
53
+ # @param collection [Enumerable] A collection of Hash or Hash-like objects
54
+ # @param options [Hash]
55
+ # @return [Object, Nil]
56
+ def pick(collection, options = {})
57
+ return nil unless collection.any?
58
+ ensure_collection_uniformity!(collection)
59
+
60
+ options = options.with_indifferent_access
61
+
62
+ if options[:priority]
63
+ pick_by_priority(collection, options[:priority])
64
+ else
65
+ pick_by_mode(collection)
66
+ end
67
+ end
68
+ module_function :pick
69
+
70
+ # Find the element with the highest count of modes
71
+ #
72
+ # @param collection [Enumerable]
73
+ # @return [Object, Nil]
74
+ def pick_by_mode(collection)
75
+ return nil unless collection.any?
76
+
77
+ if collection.first.is_a?(Hash)
78
+ indiff_collection = collection
79
+ else
80
+ indiff_collection = collection.map { |element| element.to_hash.with_indifferent_access }
81
+ end
82
+
83
+ collection.max_by do |element|
84
+ if collection.first.is_a?(Hash)
85
+ indiff_element = element
86
+ else
87
+ indiff_element = element.to_hash.with_indifferent_access
88
+ end
89
+
90
+ # Build a map of modes for each existing key
91
+ modes = indiff_element.map do |key, value|
92
+ # Filter elements without a given key to avoid unintentionally nil values
93
+ values = indiff_collection.select { |el| el.has_key?(key) }.map { |el| el[key] }
94
+ [key, Twins::Utilities.mode(values)]
95
+ end
96
+ modes = Hash[modes]
97
+
98
+ # Count the number of modes present in element
99
+ modes.select { |key, mode| indiff_element[key] == mode }.count
100
+ end
101
+ end
102
+ module_function :pick_by_mode
103
+
104
+ # Find the element with the lowest overall distances
105
+ #
106
+ # @param collection [Enumerable]
107
+ # @param options [Hash]
108
+ # @return [Object, Nil]
109
+ def pick_by_priority(collection, priorities)
110
+ return nil unless collection.any?
111
+ raise ArgumentError unless priorities.is_a?(Hash)
112
+
113
+ collection.min_by do |element|
114
+ if collection.first.is_a?(Hash)
115
+ indiff_element = element
116
+ else
117
+ indiff_element = element.to_hash.with_indifferent_access
118
+ end
119
+
120
+ priorities.map do |key, value|
121
+ Twins::Utilities.distance(value, indiff_element[key])
122
+ end.sum
123
+ end
124
+ end
125
+ module_function :pick_by_priority
126
+
127
+ # @private
128
+ def ensure_collection_uniformity!(collection)
129
+ if collection.none? { |e| e.is_a?(Hash) || e.is_a?(collection.first.class) }
130
+ raise ArgumentError, "The collection's elements must all be of the same Class"
131
+ elsif collection.none? { |e| e.respond_to?(:to_hash) }
132
+ raise ArgumentError, "The collection's elements must respond to '#to_hash'"
133
+ end
134
+ end
135
+ module_function :ensure_collection_uniformity!
136
+ private_class_method :ensure_collection_uniformity!
52
137
  end
@@ -1,3 +1,3 @@
1
1
  module Twins
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twins
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philippe Dionne
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-25 00:00:00.000000000 Z
11
+ date: 2014-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport