4 ms·
Yup, you can implement the whole ordeal in C++ like this: int main(int argc, char** argv) { std::vector<std::string> strings{ "apple", "cAt", "cat"
by Nimelrian 7y ago
Yup, you can implement the whole ordeal in C++ like this:
int main(int argc, char** argv) {
std::vector<std::string> strings{
"apple", "cAt", "cat", "Dog", "apple", "dog", "Cat",
"Apple", "dOg", "banana", "cat", "dog", "apple",
};
strings.erase(
std::remove_if(
std::begin(strings),
std::end(strings),
[seen = std::unordered_set<std::string>{}](std::string_view str) mutable {
std::string lower = "";
std::transform(std::begin(str), std::end(str), std::back_inserter(lower), ::tolower);
if (seen.find(lower) == std::end(seen)) {
seen.insert(lower);
return false;
}
return true;
}
),
std::end(strings)
);
for (auto& str : strings) {
std::cout << str << "\n";
};
}
- johannkokos 7y agoif (seen.find(lower) == std::end(seen)) { seen.insert(lower); return false; } You can just return seen.insert(lower).second as insert function return a pair<iterator, bool>
- Nimelrian 7y agoAnd today I learned something new. Been a long time since I wrote my last lines of C++ anyway.
- rndgermandude 7y agoBonus points for using emplace() instead of insert
- nicoburns 7y agoYeah, this is definitely the downside to Go. You can write more or less the same code as your C++ in Rust like this: use std::collections::HashSet; fn main () { let strings = vec![ "apple", "cAt", "cat", "Dog", "apple", "dog", "Cat", "Apple", "dOg", "banana", "cat", "dog", "apple", ]; let mut set : HashSet<&'static str> = HashSet::new(); let strings : Vec<&str> = strings .into_iter() .filter(|string| set.insert(string)) .collect(); println!("{:?}", strings); } (runnable snippet: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7d19d63ac220173cc7a40d2695841b69 https://play.rust-lang.org/?version=stable&mode=debug&editio...) EDIT: updated to make use of the fact that `set.insert` returns a boolean.
- johannkokos 7y agoJust like C++, your filter can be simplified like, filter(|string| {set.insert(string)} as insert return a bool.
- nicoburns 7y agoOo, I didn't realise that. Super handy. I've updated my snippet to make use of your suggestion :)
- DougBTX 7y agoThe output form this is a little different form the article, as it additionally requires: * that the uniqueness check is done case-insensitively, * that the casing from the first instance of the string in the input array should be used in the output, and * that the output should be ordered based on the order of the first instance of the string in the input array Possibly an unusual set of requirements (I'd generally expect that if removing duplicates from a list, that the output order wouldn't matter) but hey, different problems have different requirements. The desired output is: apple cAt Dog banana while that code snippet produces: apple cAt cat Dog dog Cat Apple dOg banana
- dbaupp 7y agoSimilar to the C++, all of those are addressed by inserting lower-cased into the set, which can be achieved via an extra method call: set.insert(string.to_lowercase()) It also requires adjusting/deleting the type annotation on the set. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cc937cb3fca05c3111beda5948e93cb6 https://play.rust-lang.org/?version=stable&mode=debug&editio...
- lasagnaphil 7y agoI've tried to fix the Rust code, but I'm getting some lifetime errors. Can anyone spot the problem? (Note: I've used C++ for most of the time and am still uncomfortable with Rust) use std::collections::HashSet; fn main () { let strings = vec![ "apple", "cAt", "cat", "Dog", "apple", "dog", "Cat", "Apple", "dOg", "banana", "cat", "dog", "apple", ]; let mut set : HashSet<&'static str> = HashSet::new(); let strings : Vec<&str> = strings .into_iter() .map(|string| (string, string.to_lowercase())) .filter(|(_, lower)| set.insert(lower)) .map(|(string, _)| string) .collect(); println!("{:?}", strings); }
- rndgermandude 7y agoThis is good enough for "small" arrays, but I wouldn't wanna run it on really large ones. You double the space with those lowercased copies + some extra space for the set itself. And you'll also spend a lot of time lowercasing the strings and std::hash'ing them for the set.
- slavik81 7y agoYou need to be careful with C's tolower. It takes a signed integer, and negative values are undefined behaviour. It would be safer to cast to unsigned char before calling tolower. https://en.cppreference.com/w/cpp/string/byte/tolower https://en.cppreference.com/w/cpp/string/byte/tolower