Skip to content

Commit 9632b4a

Browse files
committed
Add Anagram solution
1 parent f1198d7 commit 9632b4a

File tree

4 files changed

+259
-0
lines changed

4 files changed

+259
-0
lines changed

anagram/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[package]
2+
name = "anagram"
3+
version = "0.0.0"

anagram/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Anagram
2+
3+
Given a word and a list of possible anagrams, select the correct sublist.
4+
5+
Given `"listen"` and a list of candidates like `"enlists" "google"
6+
"inlets" "banana"` the program should return a list containing
7+
`"inlets"`.
8+
9+
## Rust Installation
10+
11+
Refer to the [exercism help page][help-page] for Rust installation and learning
12+
resources.
13+
14+
## Writing the Code
15+
16+
Execute the tests with:
17+
18+
```bash
19+
$ cargo test
20+
```
21+
22+
All but the first test have been ignored. After you get the first test to
23+
pass, open the tests source file which is located in the `tests` directory
24+
and remove the `#[ignore]` flag from the next test and get the tests to pass
25+
again. Each separate test is a function with `#[test]` flag above it.
26+
Continue, until you pass every test.
27+
28+
If you wish to run all tests without editing the tests source file, use:
29+
30+
```bash
31+
$ cargo test -- --ignored
32+
```
33+
34+
To run a specific test, for example `some_test`, you can use:
35+
36+
```bash
37+
$ cargo test some_test
38+
```
39+
40+
If the specific test is ignored use:
41+
42+
```bash
43+
$ cargo test some_test -- --ignored
44+
```
45+
46+
To learn more about Rust tests refer to the [online test documentation][rust-tests]
47+
48+
Make sure to read the [Modules](https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html) chapter if you
49+
haven't already, it will help you with organizing your files.
50+
51+
## Feedback, Issues, Pull Requests
52+
53+
The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help!
54+
55+
If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md).
56+
57+
[help-page]: http://exercism.io/languages/rust
58+
[modules]: https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html
59+
[cargo]: https://doc.rust-lang.org/book/second-edition/ch14-00-more-about-cargo.html
60+
[rust-tests]: https://doc.rust-lang.org/book/second-edition/ch11-02-running-tests.html
61+
62+
## Source
63+
64+
Inspired by the Extreme Startup game [https://github.com/rchatley/extreme_startup](https://github.com/rchatley/extreme_startup)
65+
66+
## Submitting Incomplete Solutions
67+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

anagram/src/lib.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use std::collections::HashSet;
2+
3+
pub fn anagrams_for<'a>(word: &str, possible_anagrams: &'a [&str]) -> HashSet<&'a str> {
4+
let anagrams = possible_anagrams.to_vec();
5+
anagrams
6+
.iter()
7+
.cloned()
8+
.filter(|&anagram| anagram.to_lowercase() != word.to_lowercase() && compare(anagram, word))
9+
.collect()
10+
}
11+
12+
fn compare(left: &str, right: &str) -> bool {
13+
let mut right = right
14+
.chars()
15+
.map(|chr| chr.to_lowercase().to_string())
16+
.collect::<Vec<_>>();
17+
let mut left = left
18+
.chars()
19+
.map(|chr| chr.to_lowercase().to_string())
20+
.collect::<Vec<_>>();
21+
left.sort();
22+
right.sort();
23+
left == right
24+
}

anagram/tests/anagrams.rs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
extern crate anagram;
2+
3+
use std::collections::HashSet;
4+
use std::iter::FromIterator;
5+
6+
fn process_anagram_case(word: &str, inputs: &[&str], expected: &[&str]) {
7+
let result = anagram::anagrams_for(word, inputs);
8+
9+
let expected: HashSet<&str> = HashSet::from_iter(expected.iter().cloned());
10+
11+
assert_eq!(result, expected);
12+
}
13+
14+
#[test]
15+
fn test_no_matches() {
16+
let word = "diaper";
17+
18+
let inputs = ["hello", "world", "zombies", "pants"];
19+
20+
let outputs = vec![];
21+
22+
process_anagram_case(word, &inputs, &outputs);
23+
}
24+
25+
#[test]
26+
fn test_detect_simple_anagram() {
27+
let word = "ant";
28+
29+
let inputs = ["tan", "stand", "at"];
30+
31+
let outputs = vec!["tan"];
32+
33+
process_anagram_case(word, &inputs, &outputs);
34+
}
35+
36+
#[test]
37+
fn test_does_not_confuse_different_duplicates() {
38+
let word = "galea";
39+
40+
let inputs = ["eagle"];
41+
42+
let outputs = vec![];
43+
44+
process_anagram_case(word, &inputs, &outputs);
45+
}
46+
47+
#[test]
48+
fn test_eliminate_anagram_subsets() {
49+
let word = "good";
50+
51+
let inputs = ["dog", "goody"];
52+
53+
let outputs = vec![];
54+
55+
process_anagram_case(word, &inputs, &outputs);
56+
}
57+
58+
#[test]
59+
fn test_detect_anagram() {
60+
let word = "listen";
61+
62+
let inputs = ["enlists", "google", "inlets", "banana"];
63+
64+
let outputs = vec!["inlets"];
65+
66+
process_anagram_case(word, &inputs, &outputs);
67+
}
68+
69+
#[test]
70+
fn test_multiple_anagrams() {
71+
let word = "allergy";
72+
73+
let inputs = [
74+
"gallery",
75+
"ballerina",
76+
"regally",
77+
"clergy",
78+
"largely",
79+
"leading",
80+
];
81+
82+
let outputs = vec!["gallery", "regally", "largely"];
83+
84+
process_anagram_case(word, &inputs, &outputs);
85+
}
86+
87+
#[test]
88+
fn test_case_insensitive_anagrams() {
89+
let word = "Orchestra";
90+
91+
let inputs = ["cashregister", "Carthorse", "radishes"];
92+
93+
let outputs = vec!["Carthorse"];
94+
95+
process_anagram_case(word, &inputs, &outputs);
96+
}
97+
98+
#[test]
99+
fn test_unicode_anagrams() {
100+
let word = "ΑΒΓ";
101+
102+
// These words don't make sense, they're just greek letters cobbled together.
103+
let inputs = ["ΒΓΑ", "ΒΓΔ", "γβα"];
104+
105+
let outputs = vec!["ΒΓΑ", "γβα"];
106+
107+
process_anagram_case(word, &inputs, &outputs);
108+
}
109+
110+
#[test]
111+
fn test_misleading_unicode_anagrams() {
112+
// Despite what a human might think these words different letters, the input uses Greek A and B
113+
// while the list of potential anagrams uses Latin A and B.
114+
let word = "ΑΒΓ";
115+
116+
let inputs = ["ABΓ"];
117+
118+
let outputs = vec![];
119+
120+
process_anagram_case(word, &inputs, &outputs);
121+
}
122+
123+
#[test]
124+
fn test_does_not_detect_a_word_as_its_own_anagram() {
125+
let word = "banana";
126+
127+
let inputs = ["banana"];
128+
129+
let outputs = vec![];
130+
131+
process_anagram_case(word, &inputs, &outputs);
132+
}
133+
134+
#[test]
135+
fn test_does_not_detect_a_differently_cased_word_as_its_own_anagram() {
136+
let word = "banana";
137+
138+
let inputs = ["bAnana"];
139+
140+
let outputs = vec![];
141+
142+
process_anagram_case(word, &inputs, &outputs);
143+
}
144+
145+
#[test]
146+
fn test_does_not_detect_a_differently_cased_unicode_word_as_its_own_anagram() {
147+
let word = "ΑΒΓ";
148+
149+
let inputs = ["ΑΒγ"];
150+
151+
let outputs = vec![];
152+
153+
process_anagram_case(word, &inputs, &outputs);
154+
}
155+
156+
#[test]
157+
fn test_same_bytes_different_chars() {
158+
let word = "a⬂"; // 61 E2 AC 82
159+
160+
let inputs = ["€a"]; // E2 82 AC 61
161+
162+
let outputs = vec![];
163+
164+
process_anagram_case(word, &inputs, &outputs);
165+
}

0 commit comments

Comments
 (0)