-
Notifications
You must be signed in to change notification settings - Fork 63
Expand file tree
/
Copy pathstring_score.coffee
More file actions
128 lines (107 loc) · 4.93 KB
/
Copy pathstring_score.coffee
File metadata and controls
128 lines (107 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
###!
* string_score.js: Quicksilver-like string scoring algorithm.
*
* Copyright (C) 2009-2011 Joshaven Potter <yourtech@gmail.com>
* Copyright (C) 2010-2011 Yesudeep Mangalapilly <yesudeep@gmail.com>
* MIT license: http://www.opensource.org/licenses/mit-license.php
###
# Special thanks to Lachie Cox and Quicksilver for inspiration.
#
# Compilation notes:
#
# 1. Compile with the `-b -c` flags to the coffee-script compiler
# `String.prototype.score`
# ------------------------
String::score = (abbreviation) ->
# **Size optimization notes**:
# Declaring `string` before checking for an exact match
# does not affect the speed and reduces size because `this`
# occurs only once in the code as a result.
string = this
# Perfect match if the string equals the abbreviation.
return 1.0 if string == abbreviation
# Initializing variables.
string_length = string.length
total_character_score = 0
# Awarded only if the string and the abbreviation have a common prefix.
should_award_common_prefix_bonus = 0 #no
#### Sum character scores
# Add up scores for each character in the abbreviation.
for c, i in abbreviation
# Find the index of current character (case-insensitive) in remaining part of string.
index_c_lowercase = string.indexOf c.toLowerCase()
index_c_uppercase = string.indexOf c.toUpperCase()
min_index = Math.min index_c_lowercase, index_c_uppercase
index_in_string = if min_index > -1 then min_index else Math.max index_c_lowercase, index_c_uppercase
#### Identical strings
# Bail out if current character is not found (case-insensitive) in remaining part of string.
#
# **Possible size optimization**:
# Replace `index_in_string == -1` with `index_in_string < 0`
# which has fewer characters and should have identical performance.
return 0 if index_in_string == -1
# Set base score for current character.
character_score = 0.1
#### Case-match bonus
# If the current abbreviation character has the same case
# as that of the character in the string, we add a bonus.
#
# **Optimization notes**:
# `charAt` was replaced with an index lookup here because
# the latter results in smaller and faster code without
# breaking any tests.
if string[index_in_string] == c
character_score += 0.1
#### Consecutive character match and common prefix bonuses
# Increase the score when each consecutive character of
# the abbreviation matches the first character of the
# remaining string.
#
# **Size optimization disabled (truthiness shortened)**:
# It produces smaller code but is slower.
#
# if !index_in_string
if index_in_string == 0
character_score += 0.8
# String and abbreviation have common prefix, so award bonus.
#
# **Size optimization disabled (truthiness shortened)**:
# It produces smaller code but is slower.
#
# if !i
if i == 0
should_award_common_prefix_bonus = 1 #yes
#### Acronym bonus
# Typing the first character of an acronym is as
# though you preceded it with two perfect character
# matches.
#
# **Size optimization disabled**:
# `string.charAt(index)` wasn't replaced with `string[index]`
# in this case even though the latter results in smaller
# code (when minified) because the former is faster, and
# the gain out of replacing it is negligible.
if string.charAt(index_in_string - 1) == ' '
character_score += 0.8 # * Math.min(index_in_string, 5) # Cap bonus at 0.4 * 5
# Left trim the matched part of the string
# (forces sequential matching).
string = string.substring(index_in_string + 1, string_length)
# Add to total character score.
total_character_score += character_score
# **Feature disabled**:
# Uncomment the following to weigh smaller words higher.
#
# return total_character_score / string_length
abbreviation_length = abbreviation.length
abbreviation_score = total_character_score / abbreviation_length
#### Reduce penalty for longer strings
# **Optimization notes (code inlined)**:
#
# percentage_of_matched_string = abbreviation_length / string_length
# word_score = abbreviation_score * percentage_of_matched_string
# final_score = (word_score + abbreviation_score) / 2
final_score = ((abbreviation_score * (abbreviation_length / string_length)) + abbreviation_score) / 2
#### Award common prefix bonus
if should_award_common_prefix_bonus and (final_score + 0.1 < 1)
final_score += 0.1
return final_score