Skip to content

Commit e540a58

Browse files
committed
Sync exercise word-count with problem spec
new tests found a bug in the example solution with apostrophe handling
1 parent 3c6e571 commit e540a58

4 files changed

Lines changed: 185 additions & 60 deletions

File tree

exercises/practice/word-count/.meta/example.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ pub fn word_count(input: &str) -> HashMap<String, u32> {
66
let slice: &str = lower.as_ref();
77
for word in slice
88
.split(|c: char| !c.is_alphanumeric() && c != '\'')
9-
.filter(|s| !s.is_empty())
109
.map(|s| s.trim_matches('\''))
10+
.filter(|s| !s.is_empty())
1111
{
1212
*map.entry(word.to_string()).or_insert(0) += 1;
1313
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std::collections::HashMap;
2+
3+
fn check_word_count(mut output: HashMap<String, u32>, pairs: &[(&str, u32)]) {
4+
// The reason for the awkward code in here is to ensure that the failure
5+
// message for assert_eq! is as informative as possible. A simpler
6+
// solution would simply check the length of the map, and then
7+
// check for the presence and value of each key in the given pairs vector.
8+
for &(k, v) in pairs.iter() {
9+
assert_eq!((k, output.remove(&k.to_string()).unwrap_or(0)), (k, v));
10+
}
11+
// may fail with a message that clearly shows all extra pairs in the map
12+
assert_eq!(output.iter().collect::<Vec<(&String, &u32)>>(), vec![]);
13+
}
14+
{% for test in cases %}
15+
#[test]
16+
{% if loop.index != 1 -%}
17+
#[ignore]
18+
{% endif -%}
19+
fn {{ test.description | slugify | replace(from="-", to="_") }}() {
20+
let input = {{ test.input.sentence | json_encode() }};
21+
let output = {{ crate_name }}::{{ fn_names[0] }}(input);
22+
let expected = &[{% for key, value in test.expected -%}
23+
({{ key | json_encode() }}, {{ value }}),
24+
{%- endfor %}];
25+
check_word_count(output, expected);
26+
}
27+
{% endfor -%}
Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,57 @@
1-
# This is an auto-generated file. Regular comments will be removed when this
2-
# file is regenerated. Regenerating will not touch any manually added keys,
3-
# so comments can be added in a "comment" key.
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[61559d5f-2cad-48fb-af53-d3973a9ee9ef]
13+
description = "count one word"
14+
15+
[5abd53a3-1aed-43a4-a15a-29f88c09cbbd]
16+
description = "count one of each word"
17+
18+
[2a3091e5-952e-4099-9fac-8f85d9655c0e]
19+
description = "multiple occurrences of a word"
20+
21+
[e81877ae-d4da-4af4-931c-d923cd621ca6]
22+
description = "handles cramped lists"
23+
24+
[7349f682-9707-47c0-a9af-be56e1e7ff30]
25+
description = "handles expanded lists"
26+
27+
[a514a0f2-8589-4279-8892-887f76a14c82]
28+
description = "ignore punctuation"
29+
30+
[d2e5cee6-d2ec-497b-bdc9-3ebe092ce55e]
31+
description = "include numbers"
32+
33+
[dac6bc6a-21ae-4954-945d-d7f716392dbf]
34+
description = "normalize case"
435

536
[4185a902-bdb0-4074-864c-f416e42a0f19]
637
description = "with apostrophes"
38+
include = false
39+
40+
[4ff6c7d7-fcfc-43ef-b8e7-34ff1837a2d3]
41+
description = "with apostrophes"
42+
reimplements = "4185a902-bdb0-4074-864c-f416e42a0f19"
743

844
[be72af2b-8afe-4337-b151-b297202e4a7b]
945
description = "with quotations"
1046

47+
[8d6815fe-8a51-4a65-96f9-2fb3f6dc6ed6]
48+
description = "substrings from the beginning"
49+
1150
[c5f4ef26-f3f7-4725-b314-855c04fb4c13]
1251
description = "multiple spaces not detected as a word"
52+
53+
[50176e8a-fe8e-4f4c-b6b6-aa9cf8f20360]
54+
description = "alternating word separators not detected as a word"
55+
56+
[6d00f1db-901c-4bec-9829-d20eb3044557]
57+
description = "quotation for word with apostrophe"
Lines changed: 109 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,169 @@
11
use std::collections::HashMap;
22

3-
fn check_word_count(s: &str, pairs: &[(&str, u32)]) {
3+
fn check_word_count(mut output: HashMap<String, u32>, pairs: &[(&str, u32)]) {
44
// The reason for the awkward code in here is to ensure that the failure
55
// message for assert_eq! is as informative as possible. A simpler
66
// solution would simply check the length of the map, and then
77
// check for the presence and value of each key in the given pairs vector.
8-
let mut m: HashMap<String, u32> = word_count::word_count(s);
98
for &(k, v) in pairs.iter() {
10-
assert_eq!((k, m.remove(&k.to_string()).unwrap_or(0)), (k, v));
9+
assert_eq!((k, output.remove(&k.to_string()).unwrap_or(0)), (k, v));
1110
}
1211
// may fail with a message that clearly shows all extra pairs in the map
13-
assert_eq!(m.iter().collect::<Vec<(&String, &u32)>>(), vec![]);
12+
assert_eq!(output.iter().collect::<Vec<(&String, &u32)>>(), vec![]);
1413
}
1514

1615
#[test]
1716
fn count_one_word() {
18-
check_word_count("word", &[("word", 1)]);
17+
let input = "word";
18+
let output = word_count::word_count(input);
19+
let expected = &[("word", 1)];
20+
check_word_count(output, expected);
1921
}
2022

2123
#[test]
2224
#[ignore]
23-
fn count_one_of_each() {
24-
check_word_count("one of each", &[("one", 1), ("of", 1), ("each", 1)]);
25+
fn count_one_of_each_word() {
26+
let input = "one of each";
27+
let output = word_count::word_count(input);
28+
let expected = &[("one", 1), ("of", 1), ("each", 1)];
29+
check_word_count(output, expected);
2530
}
2631

2732
#[test]
2833
#[ignore]
29-
fn count_multiple_occurrences() {
30-
check_word_count(
31-
"one fish two fish red fish blue fish",
32-
&[("one", 1), ("fish", 4), ("two", 1), ("red", 1), ("blue", 1)],
33-
);
34+
fn multiple_occurrences_of_a_word() {
35+
let input = "one fish two fish red fish blue fish";
36+
let output = word_count::word_count(input);
37+
let expected = &[("one", 1), ("fish", 4), ("two", 1), ("red", 1), ("blue", 1)];
38+
check_word_count(output, expected);
3439
}
3540

3641
#[test]
3742
#[ignore]
38-
fn cramped_lists() {
39-
check_word_count("one,two,three", &[("one", 1), ("two", 1), ("three", 1)]);
43+
fn handles_cramped_lists() {
44+
let input = "one,two,three";
45+
let output = word_count::word_count(input);
46+
let expected = &[("one", 1), ("two", 1), ("three", 1)];
47+
check_word_count(output, expected);
4048
}
4149

4250
#[test]
4351
#[ignore]
44-
fn expanded_lists() {
45-
check_word_count("one\ntwo\nthree", &[("one", 1), ("two", 1), ("three", 1)]);
52+
fn handles_expanded_lists() {
53+
let input = "one,\ntwo,\nthree";
54+
let output = word_count::word_count(input);
55+
let expected = &[("one", 1), ("two", 1), ("three", 1)];
56+
check_word_count(output, expected);
4657
}
4758

4859
#[test]
4960
#[ignore]
5061
fn ignore_punctuation() {
51-
check_word_count(
52-
"car : carpet as java : javascript!!&@$%^&",
53-
&[
54-
("car", 1),
55-
("carpet", 1),
56-
("as", 1),
57-
("java", 1),
58-
("javascript", 1),
59-
],
60-
);
62+
let input = "car: carpet as java: javascript!!&@$%^&";
63+
let output = word_count::word_count(input);
64+
let expected = &[
65+
("car", 1),
66+
("carpet", 1),
67+
("as", 1),
68+
("java", 1),
69+
("javascript", 1),
70+
];
71+
check_word_count(output, expected);
6172
}
6273

6374
#[test]
6475
#[ignore]
6576
fn include_numbers() {
66-
check_word_count(
67-
"testing, 1, 2 testing",
68-
&[("testing", 2), ("1", 1), ("2", 1)],
69-
);
77+
let input = "testing, 1, 2 testing";
78+
let output = word_count::word_count(input);
79+
let expected = &[("testing", 2), ("1", 1), ("2", 1)];
80+
check_word_count(output, expected);
7081
}
7182

7283
#[test]
7384
#[ignore]
7485
fn normalize_case() {
75-
check_word_count("go Go GO Stop stop", &[("go", 3), ("stop", 2)]);
86+
let input = "go Go GO Stop stop";
87+
let output = word_count::word_count(input);
88+
let expected = &[("go", 3), ("stop", 2)];
89+
check_word_count(output, expected);
7690
}
7791

7892
#[test]
7993
#[ignore]
8094
fn with_apostrophes() {
81-
check_word_count(
82-
"First: don't laugh. Then: don't cry.",
83-
&[
84-
("first", 1),
85-
("don't", 2),
86-
("laugh", 1),
87-
("then", 1),
88-
("cry", 1),
89-
],
90-
);
95+
let input = "'First: don't laugh. Then: don't cry. You're getting it.'";
96+
let output = word_count::word_count(input);
97+
let expected = &[
98+
("first", 1),
99+
("don't", 2),
100+
("laugh", 1),
101+
("then", 1),
102+
("cry", 1),
103+
("you're", 1),
104+
("getting", 1),
105+
("it", 1),
106+
];
107+
check_word_count(output, expected);
91108
}
92109

93110
#[test]
94111
#[ignore]
95112
fn with_quotations() {
96-
check_word_count(
97-
"Joe can't tell between 'large' and large.",
98-
&[
99-
("joe", 1),
100-
("can't", 1),
101-
("tell", 1),
102-
("between", 1),
103-
("large", 2),
104-
("and", 1),
105-
],
106-
);
113+
let input = "Joe can't tell between 'large' and large.";
114+
let output = word_count::word_count(input);
115+
let expected = &[
116+
("joe", 1),
117+
("can't", 1),
118+
("tell", 1),
119+
("between", 1),
120+
("large", 2),
121+
("and", 1),
122+
];
123+
check_word_count(output, expected);
124+
}
125+
126+
#[test]
127+
#[ignore]
128+
fn substrings_from_the_beginning() {
129+
let input = "Joe can't tell between app, apple and a.";
130+
let output = word_count::word_count(input);
131+
let expected = &[
132+
("joe", 1),
133+
("can't", 1),
134+
("tell", 1),
135+
("between", 1),
136+
("app", 1),
137+
("apple", 1),
138+
("and", 1),
139+
("a", 1),
140+
];
141+
check_word_count(output, expected);
107142
}
108143

109144
#[test]
110145
#[ignore]
111146
fn multiple_spaces_not_detected_as_a_word() {
112-
check_word_count(
113-
" multiple whitespaces",
114-
&[("multiple", 1), ("whitespaces", 1)],
115-
);
147+
let input = " multiple whitespaces";
148+
let output = word_count::word_count(input);
149+
let expected = &[("multiple", 1), ("whitespaces", 1)];
150+
check_word_count(output, expected);
151+
}
152+
153+
#[test]
154+
#[ignore]
155+
fn alternating_word_separators_not_detected_as_a_word() {
156+
let input = ",\n,one,\n ,two \n 'three'";
157+
let output = word_count::word_count(input);
158+
let expected = &[("one", 1), ("two", 1), ("three", 1)];
159+
check_word_count(output, expected);
160+
}
161+
162+
#[test]
163+
#[ignore]
164+
fn quotation_for_word_with_apostrophe() {
165+
let input = "can, can't, 'can't'";
166+
let output = word_count::word_count(input);
167+
let expected = &[("can", 1), ("can't", 2)];
168+
check_word_count(output, expected);
116169
}

0 commit comments

Comments
 (0)