divvy_up 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +5 -1
- data/lib/divvy_up/list.rb +62 -3
- data/lib/divvy_up/version.rb +1 -1
- data/spec/divvy_up/list_spec.rb +25 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1f81d17ce9ba4eafe46a66f3aa25828e44499cc
|
4
|
+
data.tar.gz: 009551861b5cfd507505eba3496164a1a17890d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91fa9383f65ae1ebb2aff88ab916ea7c08e9a17c10d5d8bb7baaf25ff77ea0dd21d1194a5ba4ba36b101b8103c2f273ae1a8661aa43515fa65144ffd5ad42d5f
|
7
|
+
data.tar.gz: c5a48f2b4c21ce19510a7f00f2421f794c4e293e8aaa97a93ccfb5625760e77cb979a928d57ef0d9cb9526048faf7a1b07ef4bc281b57263c121df314b2c3e63
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Change Log
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
This project adheres to [Semantic Versioning](http://semver.org/).
|
4
|
+
|
5
|
+
## [Unreleased]
|
6
|
+
|
7
|
+
- Your contribution here!
|
8
|
+
|
9
|
+
## [0.1.0] - 2015-07-04
|
10
|
+
### Changed
|
11
|
+
- Process list through two additional algorithms to handle edge cases and ensure optimal results when using `#split` method
|
12
|
+
|
13
|
+
## 0.0.1 - 2015-03-09
|
14
|
+
- Initial release
|
15
|
+
|
16
|
+
[unreleased]: https://github.com/djpowers/divvy_up/compare/v0.1.0...HEAD
|
17
|
+
[0.1.0]: https://github.com/djpowers/divvy_up/compare/v0.0.1...v0.1.0
|
data/README.md
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
# DivvyUp
|
2
2
|
|
3
|
+
[](http://badge.fury.io/rb/divvy_up)
|
4
|
+
|
3
5
|
A Ruby gem to divvy up a list of item prices into smaller groups,
|
4
|
-
for the purpose of splitting up purchases (
|
6
|
+
for the purpose of splitting up purchases (somewhat) equally.
|
7
|
+
|
8
|
+
A variation of sorts on the [knapsack problem](http://en.wikipedia.org/wiki/Knapsack_problem).
|
5
9
|
|
6
10
|
## Installation
|
7
11
|
|
data/lib/divvy_up/list.rb
CHANGED
@@ -8,14 +8,54 @@ module DivvyUp
|
|
8
8
|
|
9
9
|
def split(groups)
|
10
10
|
return [self.items] if groups == 1
|
11
|
+
determine_best_result(groups)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def permute(groups)
|
11
17
|
permutations = generate_list_permutations
|
12
18
|
permutation_price_differences = calculate_permutation_price_differences(permutations, groups)
|
13
19
|
sorted_price_differences = generate_list_combinations(permutation_price_differences)
|
14
20
|
list_possibilities = find_full_list(permutation_price_differences, sorted_price_differences)
|
15
|
-
|
21
|
+
calculate_list_totals(list_possibilities, groups)
|
16
22
|
end
|
17
23
|
|
18
|
-
|
24
|
+
def snake(groups)
|
25
|
+
unassigned_items = self.items.sort_by { |item, price| price }.to_a
|
26
|
+
permutations = []
|
27
|
+
groups.times { permutations << {} }
|
28
|
+
until unassigned_items.empty? do
|
29
|
+
permutations.each do |permutation|
|
30
|
+
permutation[unassigned_items.last.first] = unassigned_items.last.last
|
31
|
+
unassigned_items.pop
|
32
|
+
end
|
33
|
+
permutations.reverse_each do |permutation|
|
34
|
+
permutation[unassigned_items.last.first] = unassigned_items.last.last unless unassigned_items.empty?
|
35
|
+
unassigned_items.pop
|
36
|
+
end
|
37
|
+
end
|
38
|
+
calculate_list_totals(permutations, groups)
|
39
|
+
end
|
40
|
+
|
41
|
+
def price_is_right(groups)
|
42
|
+
unassigned_items = self.items.sort_by { |item, price| price }.to_a
|
43
|
+
permutations = []
|
44
|
+
groups.times { permutations << {} }
|
45
|
+
permutations.each do |permutation|
|
46
|
+
total = 0
|
47
|
+
until total > target_amount(groups) || unassigned_items.empty? do
|
48
|
+
next_item = unassigned_items.pop
|
49
|
+
permutation[next_item.first] = next_item.last
|
50
|
+
total = permutation.values.reduce(:+).nil? ? 0 : permutation.values.reduce(:+)
|
51
|
+
if total > target_amount(groups)
|
52
|
+
permutation.delete(next_item.first)
|
53
|
+
unassigned_items << next_item
|
54
|
+
end unless permutation == permutations.last || permutation.count == 1
|
55
|
+
end
|
56
|
+
end
|
57
|
+
calculate_list_totals(permutations, groups)
|
58
|
+
end
|
19
59
|
|
20
60
|
def target_amount(divisor)
|
21
61
|
(self.items.values.reduce(:+) / divisor).round(2)
|
@@ -74,7 +114,7 @@ module DivvyUp
|
|
74
114
|
output
|
75
115
|
end
|
76
116
|
|
77
|
-
def
|
117
|
+
def calculate_list_totals(lists, groups)
|
78
118
|
output = []
|
79
119
|
accounted_items = []
|
80
120
|
until output.size == groups
|
@@ -88,5 +128,24 @@ module DivvyUp
|
|
88
128
|
end
|
89
129
|
output
|
90
130
|
end
|
131
|
+
|
132
|
+
def determine_best_result(groups)
|
133
|
+
permute_result = permute(groups)
|
134
|
+
snake_result = snake(groups)
|
135
|
+
price_is_right_result = price_is_right(groups)
|
136
|
+
results = [permute_result, snake_result, price_is_right_result]
|
137
|
+
target = target_amount(groups)
|
138
|
+
result_differences = []
|
139
|
+
results.each do |result|
|
140
|
+
result_totals = []
|
141
|
+
result.each do |list|
|
142
|
+
result_totals << list.last
|
143
|
+
end
|
144
|
+
min_difference = (result_totals.min - target).abs
|
145
|
+
max_difference = (result_totals.max - target).abs
|
146
|
+
result_differences << (min_difference > max_difference ? min_difference : max_difference)
|
147
|
+
end
|
148
|
+
results[result_differences.index(result_differences.min)]
|
149
|
+
end
|
91
150
|
end
|
92
151
|
end
|
data/lib/divvy_up/version.rb
CHANGED
data/spec/divvy_up/list_spec.rb
CHANGED
@@ -42,7 +42,7 @@ module DivvyUp
|
|
42
42
|
[{bananas: 2.40, pears: 3.20}, 5.60]])
|
43
43
|
end
|
44
44
|
|
45
|
-
it "
|
45
|
+
it "splits a list into three groups" do
|
46
46
|
list = DivvyUp::List.new(shopping_list)
|
47
47
|
expect(list.split(3)).to eql(
|
48
48
|
[
|
@@ -52,5 +52,29 @@ module DivvyUp
|
|
52
52
|
]
|
53
53
|
)
|
54
54
|
end
|
55
|
+
|
56
|
+
it "splits a list using the 'snake' technique" do
|
57
|
+
shopping_list = { orange_juice: 3, colored_pepper: 1.99, spring_mix_7_oz: 4.99, pasta_sauce: 2.50, blueberries: 3.99, onion: 1.25 }
|
58
|
+
list = DivvyUp::List.new(shopping_list)
|
59
|
+
expect(list.split(3)).to eql(
|
60
|
+
[
|
61
|
+
[{spring_mix_7_oz: 4.99, onion: 1.25}, 6.24],
|
62
|
+
[{blueberries: 3.99, colored_pepper: 1.99}, 5.98],
|
63
|
+
[{orange_juice: 3, pasta_sauce: 2.5}, 5.5]
|
64
|
+
]
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "splits a list using the 'price is right' technique" do
|
69
|
+
shopping_list = { milk: 2.49, bread: 1.99, cheese: 3.50, celery: 2.99, yogurt: 2.99, ham: 4.99, potatoes: 2, belvita_bars: 3.49, rice: 5, chicken: 6.49 }
|
70
|
+
list = DivvyUp::List.new(shopping_list)
|
71
|
+
expect(list.split(3)).to eql(
|
72
|
+
[
|
73
|
+
[{chicken: 6.49, rice: 5}, 11.49],
|
74
|
+
[{ham: 4.99, cheese: 3.5, belvita_bars: 3.49}, 11.98],
|
75
|
+
[{yogurt: 2.99, celery: 2.99, milk: 2.49, potatoes: 2, bread: 1.99}, 12.46]
|
76
|
+
]
|
77
|
+
)
|
78
|
+
end
|
55
79
|
end
|
56
80
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: divvy_up
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dave Powers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -75,6 +75,7 @@ extra_rdoc_files: []
|
|
75
75
|
files:
|
76
76
|
- ".gitignore"
|
77
77
|
- ".rspec"
|
78
|
+
- CHANGELOG.md
|
78
79
|
- Gemfile
|
79
80
|
- LICENSE.txt
|
80
81
|
- README.md
|
@@ -105,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
106
|
version: '0'
|
106
107
|
requirements: []
|
107
108
|
rubyforge_project:
|
108
|
-
rubygems_version: 2.4.
|
109
|
+
rubygems_version: 2.4.5
|
109
110
|
signing_key:
|
110
111
|
specification_version: 4
|
111
112
|
summary: Divvy up purchases to split amongst friends
|