1 //! This library implements string similarity metrics.
4 use std
::cmp
::{max, min}
;
5 use std
::collections
::HashMap
;
7 #[derive(Debug, PartialEq)]
12 pub type HammingResult
= Result
<usize, StrSimError
>;
14 /// Calculates the number of positions in the two strings where the characters
15 /// differ. Returns an error if the strings have different lengths.
18 /// use strsim::hamming;
20 /// match hamming("hamming", "hammers") {
21 /// Ok(distance) => assert_eq!(3, distance),
22 /// Err(why) => panic!("{:?}", why)
25 pub fn hamming(a
: &str, b
: &str) -> HammingResult
{
26 let (mut ita
, mut itb
, mut count
) = (a
.chars(), b
.chars(), 0);
28 match (ita
.next(), itb
.next()){
29 (Some(x
), Some(y
)) => if x
!= y { count += 1 }
,
30 (None
, None
) => return Ok(count
),
31 _
=> return Err(StrSimError
::DifferentLengthArgs
),
36 /// Calculates the Jaro similarity between two strings. The returned value
37 /// is between 0.0 and 1.0 (higher value means more similar).
42 /// assert!((0.392 - jaro("Friedrich Nietzsche", "Jean-Paul Sartre")).abs() <
45 pub fn jaro(a
: &str, b
: &str) -> f64 {
46 if a
== b { return 1.0; }
48 let a_len
= a
.chars().count();
49 let b_len
= b
.chars().count();
51 // The check for lengths of one here is to prevent integer overflow when
52 // calculating the search range.
53 if a_len
== 0 || b_len
== 0 || (a_len
== 1 && b_len
== 1) {
57 let search_range
= (max(a_len
, b_len
) / 2) - 1;
59 let mut b_consumed
= Vec
::with_capacity(b_len
);
61 b_consumed
.push(false);
63 let mut matches
= 0.0;
65 let mut transpositions
= 0.0;
66 let mut b_match_index
= 0;
68 for (i
, a_char
) in a
.chars().enumerate() {
70 // prevent integer wrapping
72 max(0, i
- search_range
)
77 let max_bound
= min(b_len
- 1, i
+ search_range
);
79 if min_bound
> max_bound
{
83 for (j
, b_char
) in b
.chars().enumerate() {
84 if min_bound
<= j
&& j
<= max_bound
&& a_char
== b_char
&&
89 if j
< b_match_index
{
90 transpositions
+= 1.0;
102 (1.0 / 3.0) * ((matches
/ a_len
as f64) +
103 (matches
/ b_len
as f64) +
104 ((matches
- transpositions
) / matches
))
108 /// Calculates the Jaro distance between a string and each string in a vector.
109 /// Returns a vector of corresponding values between 0.0 and 1.0 (higher value
110 /// means more similar).
113 /// use strsim::jaro_against_vec;
115 /// let v = vec!["test", "test1", "test12", "test123", "", "tset"];
116 /// let result = jaro_against_vec("test", &v);
117 /// let expected = vec![1.0, 0.933333, 0.888889, 0.857143, 0.0, 0.916667];
118 /// let delta: f64 = result.iter()
119 /// .zip(expected.iter())
120 /// .map(|(x, y)| (x - y).abs() as f64)
121 /// .fold(0.0, |x, y| x + y as f64);
122 /// assert!(delta.abs() < 0.0001);
124 pub fn jaro_against_vec(a
: &str, v
: &[&str]) -> Vec
<f64> {
125 v
.iter().map(|b
| jaro(a
, b
)).collect()
128 /// Like Jaro but gives a boost to strings that have a common prefix.
131 /// use strsim::jaro_winkler;
133 /// assert!((0.911 - jaro_winkler("cheeseburger", "cheese fries")).abs() <
136 pub fn jaro_winkler(a
: &str, b
: &str) -> f64 {
137 let jaro_distance
= jaro(a
, b
);
139 // Don't limit the length of the common prefix
140 let prefix_length
= a
.chars()
142 .take_while(|&(a_char
, b_char
)| a_char
== b_char
)
145 let jaro_winkler_distance
=
146 jaro_distance
+ (0.1 * prefix_length
as f64 * (1.0 - jaro_distance
));
148 if jaro_winkler_distance
<= 1.0 {
149 jaro_winkler_distance
155 /// Calculates the Jaro-Winkler distances between a string and each string
156 /// in a vector. Returns a vector of corresponding values.
159 /// use strsim::jaro_winkler_against_vec;
161 /// let v = vec!["test", "test1", "test12", "test123", "", "tset"];
162 /// let result = jaro_winkler_against_vec("test", &v);
163 /// let expected = vec![1.0, 0.96, 0.933333, 0.914286, 0.0, 0.925];
164 /// let delta: f64 = result.iter()
165 /// .zip(expected.iter())
166 /// .map(|(x, y)| (x - y).abs() as f64)
167 /// .fold(0.0, |x, y| x + y as f64);
168 /// assert!(delta.abs() < 0.0001);
170 pub fn jaro_winkler_against_vec(a
: &str, v
: &[&str]) -> Vec
<f64> {
171 v
.iter().map(|b
| jaro_winkler(a
, b
)).collect()
174 /// Calculates the minimum number of insertions, deletions, and substitutions
175 /// required to change one string into the other.
178 /// use strsim::levenshtein;
180 /// assert_eq!(3, levenshtein("kitten", "sitting"));
182 pub fn levenshtein(a
: &str, b
: &str) -> usize {
183 let a_len
= a
.chars().count();
184 let b_len
= b
.chars().count();
185 if a
== b { return 0; }
186 else if a_len
== 0 { return b_len; }
187 else if b_len
== 0 { return a_len; }
189 let mut prev_distances
: Vec
<usize> = Vec
::with_capacity(b_len
+ 1);
190 let mut curr_distances
: Vec
<usize> = Vec
::with_capacity(b_len
+ 1);
192 for i
in 0..(b_len
+ 1) {
193 prev_distances
.push(i
);
194 curr_distances
.push(0);
197 for (i
, a_char
) in a
.chars().enumerate() {
198 curr_distances
[0] = i
+ 1;
200 for (j
, b_char
) in b
.chars().enumerate() {
201 let cost
= if a_char
== b_char { 0 }
else { 1 }
;
202 curr_distances
[j
+ 1] = min(curr_distances
[j
] + 1,
203 min(prev_distances
[j
+ 1] + 1,
204 prev_distances
[j
] + cost
));
207 prev_distances
.clone_from(&curr_distances
);
210 curr_distances
[b_len
]
213 /// Calculates the Levenshtein distance between a string and each string in a
214 /// vector. Returns a vector of corresponding values.
217 /// use strsim::levenshtein_against_vec;
219 /// let v = vec!["test", "test1", "test12", "test123", "", "tset"];
220 /// let result = levenshtein_against_vec("test", &v);
221 /// let expected = vec![0, 1, 2, 3, 4, 2];
222 /// assert_eq!(expected, result);
224 pub fn levenshtein_against_vec(a
: &str, v
: &[&str]) -> Vec
<usize> {
225 v
.iter().map(|b
| levenshtein(a
, b
)).collect()
228 /// Like Levenshtein but allows for adjacent transpositions. Each substring can
229 /// only be edited once.
232 /// use strsim::osa_distance;
234 /// assert_eq!(3, osa_distance("ab", "bca"));
236 pub fn osa_distance(a
: &str, b
: &str) -> usize {
237 let a_len
= a
.chars().count();
238 let b_len
= b
.chars().count();
239 if a
== b { return 0; }
240 else if a_len
== 0 { return b_len; }
241 else if b_len
== 0 { return a_len; }
243 let mut prev_two_distances
: Vec
<usize> = Vec
::with_capacity(b_len
+ 1);
244 let mut prev_distances
: Vec
<usize> = Vec
::with_capacity(b_len
+ 1);
245 let mut curr_distances
: Vec
<usize> = Vec
::with_capacity(b_len
+ 1);
247 let mut prev_a_char
= char::MAX
;
248 let mut prev_b_char
= char::MAX
;
250 for i
in 0..(b_len
+ 1) {
251 prev_two_distances
.push(i
);
252 prev_distances
.push(i
);
253 curr_distances
.push(0);
256 for (i
, a_char
) in a
.chars().enumerate() {
257 curr_distances
[0] = i
+ 1;
259 for (j
, b_char
) in b
.chars().enumerate() {
260 let cost
= if a_char
== b_char { 0 }
else { 1 }
;
261 curr_distances
[j
+ 1] = min(curr_distances
[j
] + 1,
262 min(prev_distances
[j
+ 1] + 1,
263 prev_distances
[j
] + cost
));
264 if i
> 0 && j
> 0 && a_char
!= b_char
&&
265 a_char
== prev_b_char
&& b_char
== prev_a_char
{
266 curr_distances
[j
+ 1] = min(curr_distances
[j
+ 1],
267 prev_two_distances
[j
- 1] + 1);
270 prev_b_char
= b_char
;
273 prev_two_distances
.clone_from(&prev_distances
);
274 prev_distances
.clone_from(&curr_distances
);
275 prev_a_char
= a_char
;
278 curr_distances
[b_len
]
282 /// Calculates the optimal string alignment distance between a string and each
283 /// string in a vector. Returns a vector of corresponding values.
286 /// use strsim::osa_distance_against_vec;
288 /// let v = vec!["test", "test1", "test12", "test123", "", "tset"];
289 /// let result = osa_distance_against_vec("test", &v);
290 /// let expected = vec![0, 1, 2, 3, 4, 1];
291 /// assert_eq!(expected, result);
293 pub fn osa_distance_against_vec(a
: &str, v
: &[&str]) -> Vec
<usize> {
294 v
.iter().map(|b
| osa_distance(a
, b
)).collect()
297 /// Like optimal string alignment, but substrings can be edited an unlimited
298 /// number of times, and the triangle inequality holds.
301 /// use strsim::damerau_levenshtein;
303 /// assert_eq!(2, damerau_levenshtein("ab", "bca"));
305 pub fn damerau_levenshtein(a
: &str, b
: &str) -> usize {
306 if a
== b { return 0; }
308 let a_chars
: Vec
<char> = a
.chars().collect();
309 let b_chars
: Vec
<char> = b
.chars().collect();
310 let a_len
= a_chars
.len();
311 let b_len
= b_chars
.len();
313 if a_len
== 0 { return b_len; }
314 if b_len
== 0 { return a_len; }
316 let mut distances
= vec
![vec
![0; b_len
+ 2]; a_len
+ 2];
317 let max_distance
= a_len
+ b_len
;
318 distances
[0][0] = max_distance
;
320 for i
in 0..(a_len
+ 1) {
321 distances
[i
+ 1][0] = max_distance
;
322 distances
[i
+ 1][1] = i
;
325 for j
in 0..(b_len
+ 1) {
326 distances
[0][j
+ 1] = max_distance
;
327 distances
[1][j
+ 1] = j
;
330 let mut chars
: HashMap
<char, usize> = HashMap
::new();
332 for i
in 1..(a_len
+ 1) {
335 for j
in 1..(b_len
+ 1) {
336 let k
= match chars
.get(&b_chars
[j
- 1]) {
337 Some(value
) => value
.clone(),
344 if a_chars
[i
- 1] == b_chars
[j
- 1] {
349 let substitution_cost
= distances
[i
][j
] + cost
;
350 let insertion_cost
= distances
[i
][j
+ 1] + 1;
351 let deletion_cost
= distances
[i
+ 1][j
] + 1;
352 let transposition_cost
= distances
[k
][l
] + (i
- k
- 1) + 1 +
355 distances
[i
+ 1][j
+ 1] = min(substitution_cost
,
358 transposition_cost
)));
361 chars
.insert(a_chars
[i
- 1], i
);
364 distances
[a_len
+ 1][b_len
+ 1]
367 /// Calculates the Damerau-Levenshtein distance between a string and each string
368 /// in a vector. Returns a vector of corresponding values.
371 /// use strsim::damerau_levenshtein_against_vec;
373 /// let v = vec!["test", "test1", "test12", "test123", "", "tset"];
374 /// let result = damerau_levenshtein_against_vec("test", &v);
375 /// let expected = vec![0, 1, 2, 3, 4, 1];
376 /// assert_eq!(expected, result);
378 pub fn damerau_levenshtein_against_vec(a
: &str, v
: &[&str]) -> Vec
<usize> {
379 v
.iter().map(|b
| damerau_levenshtein(a
, b
)).collect()
388 match hamming("", "") {
389 Ok(distance
) => { assert_eq!(0, distance); }
,
390 Err(why
) => { panic!("{:?}
", why); }
396 match hamming("hamming
", "hamming
") {
397 Ok(distance) => { assert_eq!(0, distance); },
398 Err(why) => { panic!("{:?}", why
); }
404 match hamming("hamming", "hammers") {
405 Ok(distance
) => { assert_eq!(3, distance); }
,
406 Err(why
) => { panic!("{:?}
", why); }
411 fn hamming_diff_multibyte() {
412 match hamming("hamming
", "h香mmüng
") {
413 Ok(distance) => { assert_eq!(2, distance); },
414 Err(why) => { panic!("{:?}", why
); }
419 fn hamming_unequal_length() {
420 match hamming("ham", "hamming") {
421 Ok(_
) => { panic!(); }
,
422 Err(why
) => { assert_eq!(why, StrSimError::DifferentLengthArgs); }
428 match hamming("Friedrich Nietzs", "Jean-Paul Sartre") {
429 Ok(distance
) => { assert_eq!(14, distance); }
,
430 Err(why
) => { panic!("{:?}
", why); }
435 fn jaro_both_empty() {
436 assert_eq!(1.0, jaro("", ""));
440 fn jaro_first_empty() {
441 assert_eq!(0.0, jaro("", "jaro
"));
445 fn jaro_second_empty() {
446 assert_eq!(0.0, jaro("distance
", ""));
451 assert_eq!(1.0, jaro("jaro
", "jaro
"));
455 fn jaro_multibyte() {
456 assert!((0.818 - jaro("testabctest
", "testöঙ香test
")) < 0.001);
457 assert!((0.818 - jaro("testöঙ香test
", "testabctest
")) < 0.001);
461 fn jaro_diff_short() {
462 assert!((0.767 - jaro("dixon
", "dicksonx
")).abs() < 0.001);
466 fn jaro_diff_one_character() {
467 assert_eq!(0.0, jaro("a
", "b
"));
471 fn jaro_diff_one_and_two() {
472 assert!((0.83 - jaro("a
", "ab
")).abs() < 0.01);
476 fn jaro_diff_two_and_one() {
477 assert!((0.83 - jaro("ab
", "a
")).abs() < 0.01);
481 fn jaro_diff_no_transposition() {
482 assert!((0.822 - jaro("dwayne
", "duane
")).abs() < 0.001);
486 fn jaro_diff_with_transposition() {
487 assert!((0.944 - jaro("martha
", "marhta
")).abs() < 0.001);
492 assert!((0.392 - jaro("Friedrich Nietzsche
",
493 "Jean
-Paul Sartre
")).abs() < 0.001);
497 fn jaro_winkler_both_empty() {
498 assert_eq!(1.0, jaro_winkler("", ""));
502 fn jaro_winkler_first_empty() {
503 assert_eq!(0.0, jaro_winkler("", "jaro
-winkler
"));
507 fn jaro_winkler_second_empty() {
508 assert_eq!(0.0, jaro_winkler("distance
", ""));
512 fn jaro_winkler_same() {
513 assert_eq!(1.0, jaro_winkler("Jaro
-Winkler
", "Jaro
-Winkler
"));
517 fn jaro_winkler_multibyte() {
518 assert!((0.89 - jaro_winkler("testabctest
", "testöঙ香test
")).abs() <
520 assert!((0.89 - jaro_winkler("testöঙ香test
", "testabctest
")).abs() <
525 fn jaro_winkler_diff_short() {
526 assert!((0.813 - jaro_winkler("dixon
", "dicksonx
")).abs() < 0.001);
527 assert!((0.813 - jaro_winkler("dicksonx
", "dixon
")).abs() < 0.001);
531 fn jaro_winkler_diff_one_character() {
532 assert_eq!(0.0, jaro_winkler("a
", "b
"));
536 fn jaro_winkler_diff_no_transposition() {
537 assert!((0.840 - jaro_winkler("dwayne
", "duane
")).abs() < 0.001);
541 fn jaro_winkler_diff_with_transposition() {
542 assert!((0.961 - jaro_winkler("martha
", "marhta
")).abs() < 0.001);
546 fn jaro_winkler_names() {
547 assert!((0.562 - jaro_winkler("Friedrich Nietzsche
",
548 "Fran
-Paul Sartre
")).abs() < 0.001);
552 fn jaro_winkler_long_prefix() {
553 assert!((0.911 - jaro_winkler("cheeseburger
", "cheese fries
")).abs() <
558 fn jaro_winkler_more_names() {
559 assert!((0.868 - jaro_winkler("Thorkel
", "Thorgier
")).abs() < 0.001);
563 fn jaro_winkler_length_of_one() {
564 assert!((0.738 - jaro_winkler("Dinsdale
", "D
")).abs() < 0.001);
568 fn jaro_winkler_very_long_prefix() {
569 assert!((1.0 - jaro_winkler("thequickbrownfoxjumpedoverx
",
570 "thequickbrownfoxjumpedovery
")).abs() <
575 fn levenshtein_empty() {
576 assert_eq!(0, levenshtein("", ""));
580 fn levenshtein_same() {
581 assert_eq!(0, levenshtein("levenshtein
", "levenshtein
"));
585 fn levenshtein_diff_short() {
586 assert_eq!(3, levenshtein("kitten
", "sitting
"));
590 fn levenshtein_diff_with_space() {
591 assert_eq!(5, levenshtein("hello
, world
", "bye
, world
"));
595 fn levenshtein_diff_multibyte() {
596 assert_eq!(3, levenshtein("öঙ香
", "abc
"));
597 assert_eq!(3, levenshtein("abc
", "öঙ香
"));
601 fn levenshtein_diff_longer() {
602 let a = "The quick brown fox jumped over the angry dog
.";
603 let b = "Lorem ipsum dolor sit amet
, dicta latine an eam
.";
604 assert_eq!(37, levenshtein(a, b));
608 fn levenshtein_first_empty() {
609 assert_eq!(7, levenshtein("", "sitting
"));
613 fn levenshtein_second_empty() {
614 assert_eq!(6, levenshtein("kitten
", ""));
618 fn osa_distance_empty() {
619 assert_eq!(0, osa_distance("", ""));
623 fn osa_distance_same() {
624 assert_eq!(0, osa_distance("damerau
", "damerau
"));
628 fn osa_distance_first_empty() {
629 assert_eq!(7, osa_distance("", "damerau
"));
633 fn osa_distance_second_empty() {
634 assert_eq!(7, osa_distance("damerau
", ""));
638 fn osa_distance_diff() {
639 assert_eq!(3, osa_distance("ca
", "abc
"));
643 fn osa_distance_diff_short() {
644 assert_eq!(3, osa_distance("damerau
", "aderua
"));
648 fn osa_distance_diff_reversed() {
649 assert_eq!(3, osa_distance("aderua
", "damerau
"));
653 fn osa_distance_diff_multibyte() {
654 assert_eq!(3, osa_distance("öঙ香
", "abc
"));
655 assert_eq!(3, osa_distance("abc
", "öঙ香
"));
659 fn osa_distance_diff_unequal_length() {
660 assert_eq!(6, osa_distance("damerau
", "aderuaxyz
"));
664 fn osa_distance_diff_unequal_length_reversed() {
665 assert_eq!(6, osa_distance("aderuaxyz
", "damerau
"));
669 fn osa_distance_diff_comedians() {
670 assert_eq!(5, osa_distance("Stewart
", "Colbert
"));
674 fn osa_distance_many_transpositions() {
675 assert_eq!(4, osa_distance("abcdefghijkl
", "bacedfgihjlk
"));
679 fn osa_distance_diff_longer() {
680 let a = "The quick brown fox jumped over the angry dog
.";
681 let b = "Lehem ipsum dolor sit amet
, dicta latine an eam
.";
682 assert_eq!(36, osa_distance(a, b));
686 fn osa_distance_beginning_transposition() {
687 assert_eq!(1, osa_distance("foobar
", "ofobar
"));
691 fn osa_distance_end_transposition() {
692 assert_eq!(1, osa_distance("specter
", "spectre
"));
696 fn osa_distance_restricted_edit() {
697 assert_eq!(4, osa_distance("a cat
", "an abct
"));
701 fn damerau_levenshtein_empty() {
702 assert_eq!(0, damerau_levenshtein("", ""));
706 fn damerau_levenshtein_same() {
707 assert_eq!(0, damerau_levenshtein("damerau
", "damerau
"));
711 fn damerau_levenshtein_first_empty() {
712 assert_eq!(7, damerau_levenshtein("", "damerau
"));
716 fn damerau_levenshtein_second_empty() {
717 assert_eq!(7, damerau_levenshtein("damerau
", ""));
721 fn damerau_levenshtein_diff() {
722 assert_eq!(2, damerau_levenshtein("ca
", "abc
"));
726 fn damerau_levenshtein_diff_short() {
727 assert_eq!(3, damerau_levenshtein("damerau
", "aderua
"));
731 fn damerau_levenshtein_diff_reversed() {
732 assert_eq!(3, damerau_levenshtein("aderua
", "damerau
"));
736 fn damerau_levenshtein_diff_multibyte() {
737 assert_eq!(3, damerau_levenshtein("öঙ香
", "abc
"));
738 assert_eq!(3, damerau_levenshtein("abc
", "öঙ香
"));
742 fn damerau_levenshtein_diff_unequal_length() {
743 assert_eq!(6, damerau_levenshtein("damerau
", "aderuaxyz
"));
747 fn damerau_levenshtein_diff_unequal_length_reversed() {
748 assert_eq!(6, damerau_levenshtein("aderuaxyz
", "damerau
"));
752 fn damerau_levenshtein_diff_comedians() {
753 assert_eq!(5, damerau_levenshtein("Stewart
", "Colbert
"));
757 fn damerau_levenshtein_many_transpositions() {
758 assert_eq!(4, damerau_levenshtein("abcdefghijkl
", "bacedfgihjlk
"));
762 fn damerau_levenshtein_diff_longer() {
763 let a = "The quick brown fox jumped over the angry dog
.";
764 let b = "Lehem ipsum dolor sit amet
, dicta latine an eam
.";
765 assert_eq!(36, damerau_levenshtein(a, b));
769 fn damerau_levenshtein_beginning_transposition() {
770 assert_eq!(1, damerau_levenshtein("foobar
", "ofobar
"));
774 fn damerau_levenshtein_end_transposition() {
775 assert_eq!(1, damerau_levenshtein("specter
", "spectre
"));
779 fn damerau_levenshtein_unrestricted_edit() {
780 assert_eq!(3, damerau_levenshtein("a cat
", "an abct
"));
784 fn levenshtein_against_vec_empty() {
786 let result = levenshtein_against_vec("test
", &v);
787 let expected: Vec<usize> = Vec::new();
788 assert_eq!(expected, result);
792 fn levenshtein_against_vec_one() {
793 let v = vec!["testy
"];
794 let result = levenshtein_against_vec("test
", &v);
795 let expected = vec![1];
796 assert_eq!(expected, result);
800 fn levenshtein_against_vec_many() {
801 let v = vec!["test
", "test1
", "test12
", "test123
", "", "tset
"];
802 let result = levenshtein_against_vec("test
", &v);
803 let expected = vec![0, 1, 2, 3, 4, 2];
804 assert_eq!(expected, result);
808 fn osa_distance_against_vec_empty() {
810 let result = osa_distance_against_vec("test
", &v);
811 let expected: Vec<usize> = Vec::new();
812 assert_eq!(expected, result);
816 fn osa_distance_against_vec_one() {
817 let v = vec!["etst
"];
818 let result = osa_distance_against_vec("test
", &v);
819 let expected = vec![1];
820 assert_eq!(expected, result);
824 fn osa_distance_against_vec_many() {
825 let v = vec!["test
", "test1
", "test12
", "test123
", "", "tsvet
"];
826 let result = osa_distance_against_vec("test
", &v);
827 let expected = vec![0, 1, 2, 3, 4, 3];
828 assert_eq!(expected, result);
832 fn damerau_levenshtein_against_vec_empty() {
834 let result = damerau_levenshtein_against_vec("test
", &v);
835 let expected: Vec<usize> = Vec::new();
836 assert_eq!(expected, result);
840 fn damerau_levenshtein_against_vec_one() {
841 let v = vec!["etst
"];
842 let result = damerau_levenshtein_against_vec("test
", &v);
843 let expected = vec![1];
844 assert_eq!(expected, result);
848 fn damerau_levenshtein_against_vec_many() {
849 let v = vec!["test
", "test1
", "test12
", "test123
", "", "tsvet
"];
850 let result = damerau_levenshtein_against_vec("test
", &v);
851 let expected = vec![0, 1, 2, 3, 4, 2];
852 assert_eq!(expected, result);
855 fn equal_float_vecs(a: Vec<f64>, b: Vec<f64>) -> bool {
856 let delta: f64 = a.iter()
858 .map(|(x, y)| (x - y).abs() as f64)
859 .fold(0.0, |x, y| x + y as f64);
864 fn jaro_against_vec_empty() {
866 let result = jaro_against_vec("test
", &v);
867 let expected: Vec<f64> = Vec::new();
868 assert_eq!(expected, result);
872 fn jaro_against_vec_one() {
873 let v = vec!["test1
"];
874 let result = jaro_against_vec("test
", &v);
875 let expected = vec![0.93333];
876 assert!(equal_float_vecs(result, expected));
880 fn jaro_against_vec_many() {
881 let v = vec!["test
", "test1
", "test12
", "test123
", "", "tset
"];
882 let result = jaro_against_vec("test
", &v);
883 let expected = vec![1.0, 0.933333, 0.888889, 0.857143, 0.0, 0.916667];
884 assert!(equal_float_vecs(result, expected));
888 fn jaro_winkler_against_vec_empty() {
890 let result = jaro_winkler_against_vec("test
", &v);
891 let expected: Vec<f64> = Vec::new();
892 assert_eq!(expected, result);
896 fn jaro_winkler_against_vec_one() {
897 let v = vec!["test123
"];
898 let result = jaro_winkler_against_vec("test
", &v);
899 let expected = vec![0.914286];
900 assert!(equal_float_vecs(result, expected));
904 fn jaro_winkler_against_vec_many() {
905 let v = vec!["test
", "test1
", "test12
", "test123
", "", "tset
"];
906 let result = jaro_winkler_against_vec("test
", &v);
907 let expected = vec![1.0, 0.96, 0.933333, 0.914286, 0.0, 0.925];
908 assert!(equal_float_vecs(result, expected));