divvy_up 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/divvy_up.svg)](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
|