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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 13df8fba53cef5c26b453bdfb56828dfcb70b36e
4
- data.tar.gz: 58d128d044bbca50a8a76e07775e2e893265842b
3
+ metadata.gz: a1f81d17ce9ba4eafe46a66f3aa25828e44499cc
4
+ data.tar.gz: 009551861b5cfd507505eba3496164a1a17890d9
5
5
  SHA512:
6
- metadata.gz: e5da9da7c428b2260b50931151c95609def33a8883707113572701df727dbb4b9e0e55d5e16b9ddcad28d173257e392614a2f2812e15008a97567296b39c4fae
7
- data.tar.gz: 06e9f4ed191d2759ade0ca39cc348e800cc40924a82dc3a8613165b18a3e9febdb63645c292294a3feaf2f70967d95ef97abbee2ad8529bba69464cd5ceaa405
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 (somehwat) equally.
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
- output_final_lists(list_possibilities, groups)
21
+ calculate_list_totals(list_possibilities, groups)
16
22
  end
17
23
 
18
- private
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 output_final_lists(lists, groups)
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
@@ -1,3 +1,3 @@
1
1
  module DivvyUp
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -42,7 +42,7 @@ module DivvyUp
42
42
  [{bananas: 2.40, pears: 3.20}, 5.60]])
43
43
  end
44
44
 
45
- it "split a list into three groups" do
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.1
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-03-10 00:00:00.000000000 Z
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.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