weighted_list_rank 0.3.1 → 0.4.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
  SHA256:
3
- metadata.gz: bf34a6c62fda7d3038b31da0d7c8d9ec0b9a86a1b0204308e44d950e599f95b7
4
- data.tar.gz: 3ed6316fa225185f79afa58899f2b0d29c0abb072c7835492be8dc14b1151b39
3
+ metadata.gz: f9afa895dc286e9cc5dd2370376b965eda825669debad06e21c232b996e2cab6
4
+ data.tar.gz: 6116b3e97dc978a0ca8021f683ff7d5b31b1ebd1e0e3f07fc10c7a266b34659e
5
5
  SHA512:
6
- metadata.gz: ff08f7f218c00bd592e6c54a8862df527fb8c75cbfdf28ec4ccad44221c73b3fb699f590b0e8cb82c7b9e023e42f0fd285dab907a766864b88360cfb622dcdc2
7
- data.tar.gz: 760b3d89d47fce1c4fa48899bdd9f58ee925bbe90937988028b1162f19b6afb4f157b37a7ff68b4964d6581f288834e9a065c372f68ca22bf0d31f572cda9806
6
+ metadata.gz: 71656bad8660b6ae1455afc06a406dddf69f688feb22b690b6dcef57b39aa2ba60df1fe14be91aa22af957b71960b7fe856a980b1769090ea039149fcb005abe
7
+ data.tar.gz: cb30e3d19ed531e74ff7d665c92029a105c83a6d18e3ce98671c3471218c1c5fa6fc39672394161444b38cc2a97d0dfc2aeab60cfaedefde2664f246cd9ac5c6
data/CHANGELOG.md CHANGED
@@ -1,12 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2024-06-26
4
+ - Added score_penalty feature to allow percentage-based penalties on item scores.
5
+ - Updated all dependencies to their latest versions.
6
+
3
7
  ## [0.3.1] - 2024-05-026
4
8
 
5
9
  - Fixed issue with lists with only a single item getting bonus points
6
10
 
7
11
  ## [0.3.0] - 2024-02-03
8
12
 
9
- - added bonus_pool_percentage feature to expoential
13
+ - added bonus_pool_percentage feature to exponential
10
14
 
11
15
  ## [0.2.0] - 2024-02-03
12
16
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- weighted_list_rank (0.3.1)
4
+ weighted_list_rank (0.4.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -10,18 +10,18 @@ GEM
10
10
  json (2.7.2)
11
11
  language_server-protocol (3.17.0.3)
12
12
  lint_roller (1.1.0)
13
- minitest (5.23.1)
14
- parallel (1.24.0)
15
- parser (3.3.1.0)
13
+ minitest (5.24.0)
14
+ parallel (1.25.1)
15
+ parser (3.3.3.0)
16
16
  ast (~> 2.4.1)
17
17
  racc
18
18
  racc (1.8.0)
19
19
  rainbow (3.1.1)
20
20
  rake (13.2.1)
21
21
  regexp_parser (2.9.2)
22
- rexml (3.2.8)
23
- strscan (>= 3.0.9)
24
- rubocop (1.63.5)
22
+ rexml (3.3.1)
23
+ strscan
24
+ rubocop (1.64.1)
25
25
  json (~> 2.3)
26
26
  language_server-protocol (>= 3.17.0)
27
27
  parallel (~> 1.10)
@@ -37,16 +37,16 @@ GEM
37
37
  rubocop-minitest (0.35.0)
38
38
  rubocop (>= 1.61, < 2.0)
39
39
  rubocop-ast (>= 1.31.1, < 2.0)
40
- rubocop-performance (1.21.0)
40
+ rubocop-performance (1.21.1)
41
41
  rubocop (>= 1.48.1, < 2.0)
42
42
  rubocop-ast (>= 1.31.1, < 2.0)
43
43
  rubocop-rake (0.6.0)
44
44
  rubocop (~> 1.0)
45
45
  ruby-progressbar (1.13.0)
46
- standard (1.36.0)
46
+ standard (1.39.0)
47
47
  language_server-protocol (~> 3.17.0.2)
48
48
  lint_roller (~> 1.0)
49
- rubocop (~> 1.63.0)
49
+ rubocop (~> 1.64.0)
50
50
  standard-custom (~> 1.0.0)
51
51
  standard-performance (~> 1.4)
52
52
  standard-custom (1.0.2)
data/README.md CHANGED
@@ -92,6 +92,78 @@ custom_ranked_items.each do |item|
92
92
  end
93
93
  ```
94
94
 
95
+ ### Using Item Penalties
96
+ The WeightedListRank system also supports applying penalties to individual items. A penalty is defined as a percentage reduction in the item's score. This feature can be used to de-emphasize certain items based on specific criteria.
97
+
98
+ To use the penalty feature, include the score_penalty attribute when defining your items:
99
+
100
+ ```ruby
101
+ class MyItem
102
+ include WeightedListRank::Item
103
+ attr_reader :id, :position, :score_penalty
104
+
105
+ def initialize(id, position, score_penalty = nil)
106
+ @id = id
107
+ @position = position
108
+ @score_penalty = score_penalty
109
+ end
110
+ end
111
+ ```
112
+
113
+ You can then apply the penalties when calculating the scores:
114
+
115
+ ```ruby
116
+ require 'weighted_list_rank'
117
+
118
+ # Initialize items and list with text identifiers and penalties
119
+ items = [
120
+ MyItem.new("Item 1", 1, 0.20), # 20% penalty
121
+ MyItem.new("Item 2", 2, 0.10), # 10% penalty
122
+ MyItem.new("Item 3", 3, nil) # No penalty
123
+ ]
124
+ list = MyList.new("List 1", 10, items)
125
+
126
+ # Initialize the Exponential strategy with an optional exponent
127
+ exponential_strategy = WeightedListRank::Strategies::Exponential.new(exponent: 1.5)
128
+
129
+ # Create a RankingContext using the Exponential strategy
130
+ ranking_context = WeightedListRank::RankingContext.new(exponential_strategy)
131
+
132
+ # Rank the items
133
+ ranked_items = ranking_context.rank([list])
134
+
135
+ # Display the ranked items
136
+ ranked_items.each do |item|
137
+ puts "Item: #{item[:id]}, Total Score: #{item[:total_score]}"
138
+ end
139
+ ```
140
+
141
+ ### Customizing Penalties Example
142
+ You can customize the penalty values to see how they affect the final scores of the items.
143
+
144
+ ```ruby
145
+ # Customizing penalties for different items
146
+ items = [
147
+ MyItem.new("Item 1", 1, 0.30), # 30% penalty
148
+ MyItem.new("Item 2", 2, 0.15), # 15% penalty
149
+ MyItem.new("Item 3", 3, nil) # No penalty
150
+ ]
151
+ list = MyList.new("List 1", 10, items)
152
+
153
+ # Initialize the Exponential strategy with an optional exponent
154
+ exponential_strategy = WeightedListRank::Strategies::Exponential.new(exponent: 2.0)
155
+
156
+ # Create a RankingContext using the Exponential strategy
157
+ ranking_context = WeightedListRank::RankingContext.new(exponential_strategy)
158
+
159
+ # Rank the items
160
+ ranked_items = ranking_context.rank([list])
161
+
162
+ # Output the results
163
+ ranked_items.each do |item|
164
+ puts "Item: #{item[:id]}, Total Score: #{item[:total_score]}"
165
+ end
166
+ ```
95
167
 
96
168
  ## Development
97
169
 
data/Rakefile CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "rake/testtask"
5
+ require "standard/rake"
5
6
 
6
7
  Rake::TestTask.new(:test) do |t|
7
8
  t.libs << "test"
@@ -11,5 +11,11 @@ module WeightedListRank
11
11
  def id
12
12
  raise NotImplementedError, "Implement this method to return the item's unique identifier"
13
13
  end
14
+
15
+ # Returns the score penalty of the item. Can be overridden by the including class.
16
+ # @return [Float, NilClass] the score penalty of the item as a percentage (e.g., 0.20 for 20%) or nil if no penalty
17
+ def score_penalty
18
+ nil
19
+ end
14
20
  end
15
21
  end
@@ -42,21 +42,38 @@ module WeightedListRank
42
42
  # @return [Float] the calculated score for the item, adjusted by the list's weight, the specified exponent,
43
43
  # and the bonus pool percentage.
44
44
  def calculate_score(list, item)
45
- # Return the list weight if there are no positions
46
- return list.weight if item.position.nil? || list.items.count == 1
45
+ # Default score to the list's weight
46
+ score = list.weight
47
47
 
48
- num_items = list.items.count
49
- total_bonus_pool = list.weight * bonus_pool_percentage
48
+ unless item.position.nil? || list.items.count == 1
49
+ num_items = list.items.count
50
+ total_bonus_pool = list.weight * bonus_pool_percentage
50
51
 
51
- # Calculate the exponential factor for the item's rank position
52
- exponential_factor = (num_items + 1 - item.position)**exponent
53
- total_exponential_factor = (1..num_items).sum { |pos| (num_items + 1 - pos)**exponent }
52
+ # Calculate the exponential factor for the item's rank position
53
+ exponential_factor = (num_items + 1 - item.position)**exponent
54
+ total_exponential_factor = (1..num_items).sum { |pos| (num_items + 1 - pos)**exponent }
54
55
 
55
- # Allocate a portion of the total bonus pool based on the item's exponential factor
56
- item_bonus = (exponential_factor / total_exponential_factor) * total_bonus_pool
56
+ # Allocate a portion of the total bonus pool based on the item's exponential factor
57
+ item_bonus = (exponential_factor / total_exponential_factor) * total_bonus_pool
57
58
 
58
- # The final score is the list's weight plus the item's allocated bonus
59
- list.weight + item_bonus
59
+ # Add the item's allocated bonus to the default score
60
+ score += item_bonus
61
+ end
62
+
63
+ # Apply score penalty if it exists
64
+ apply_penalty(score, item.score_penalty)
65
+ end
66
+
67
+ private
68
+
69
+ # Applies the score penalty if it exists
70
+ #
71
+ # @param score [Float] the original score of the item
72
+ # @param penalty [Float, NilClass] the score penalty of the item as a percentage (e.g., 0.20 for 20%) or nil if no penalty
73
+ #
74
+ # @return [Float] the score after applying the penalty
75
+ def apply_penalty(score, penalty)
76
+ penalty ? score * (1 - penalty) : score
60
77
  end
61
78
  end
62
79
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WeightedListRank
4
- VERSION = "0.3.1"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/weighted_list_rank/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "weighted_list_rank"
7
+ spec.version = WeightedListRank::VERSION
8
+ spec.authors = ["Shane Sherman"]
9
+ spec.email = ["shane.sherman@gmail.com"]
10
+
11
+ spec.summary = "generate ranks of items from weighted lists"
12
+ spec.description = "generate ranks of items from weighted lists"
13
+ spec.homepage = "https://github.com/ssherman/weighted_list_rank"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.6.0"
16
+
17
+ # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://github.com/ssherman/weighted_list_rank"
21
+ spec.metadata["changelog_uri"] = "https://github.com/ssherman/weighted_list_rank/CHANGELOG.md"
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
28
+ end
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ # Uncomment to register a new dependency of your gem
35
+ # spec.add_dependency "example-gem", "~> 1.0"
36
+ spec.add_development_dependency "rubocop", "~> 1.0"
37
+ spec.add_development_dependency "standard", "~> 1.0"
38
+ spec.add_development_dependency "rubocop-minitest", "~> 0.3"
39
+ spec.add_development_dependency "rubocop-rake", "~> 0.6"
40
+ # For more information and examples about making a new gem, check out our
41
+ # guide at: https://bundler.io/guides/creating_gem.html
42
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: weighted_list_rank
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Sherman
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-26 00:00:00.000000000 Z
11
+ date: 2024-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -89,6 +89,7 @@ files:
89
89
  - lib/weighted_list_rank/strategy.rb
90
90
  - lib/weighted_list_rank/version.rb
91
91
  - sig/weighted_list_rank.rbs
92
+ - weighted_list_rank.gemspec
92
93
  homepage: https://github.com/ssherman/weighted_list_rank
93
94
  licenses:
94
95
  - MIT
@@ -111,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
112
  - !ruby/object:Gem::Version
112
113
  version: '0'
113
114
  requirements: []
114
- rubygems_version: 3.3.7
115
+ rubygems_version: 3.5.11
115
116
  signing_key:
116
117
  specification_version: 4
117
118
  summary: generate ranks of items from weighted lists