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 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