How do you find the substring within a string in the key of a multimap? For example, if I enter "Louis," then Louisville, Louisberg, and StLouis are found?
For what you want to do you will have to search within each and every key, not just the prefix or suffix. I don't think there's any way round that and it cannot be optimised since the search term can occur anywhere within the key.
You can use std::find_if and supply a predicate function for matching elements and iterate your map. I am using std::map in the code below but this could apply for a std::multimap too.
#include <map>
#include <string>
#include <algorithm>
#include <iostream>
int main()
{
std::map<std::string, std::string> myMap{
{"Louis", "AA"}, {"Louisville", "BBB"}, {"Louisberg", "A"},
{"StLouis ", "C"}, {"Huntsville", "D"} };
std::string term("Louis");
auto keyContains = [&term](const std::pair<std::string, std::string>& item)
{
return item.first.find(term) != std::string::npos;
};
auto iter = std::find_if(myMap.begin(), myMap.end(), keyContains);
while (iter != myMap.end())
{
std::cout << iter->first << std::endl;
iter = std::find_if(std::next(iter), myMap.end(), keyContains);
}
}
keyContains is lambda function. If you're not familiar with lambda functions you can use a functor instead:
struct keyContains
{
keyContains(const std::string& searchTerm) : mSearchTerm(searchTerm) {}
bool operator() (const std::pair<std::string, string>& item) const
{
return item.first.find(mSearchTerm) != std::string::npos;
}
std::string mSearchTerm;
};
Then initialise it like this: keyContains comp("Louis") and pass comp as the predicate.
Hope this is helpful? It's effectively a for loop that walks the map. Working version here.
Update:
I just read your comment where you say your search should return 54049 results. That's a lot of records! To do that it is better to match against prefix or suffix. You can use std::map::lower_bound() and std::map::upper_bound().
Why bother with a multimap for that?
std::string search_term = "Louis";
std::unordered_set<std::string> data = { "Louisville", "Louisberg", "StLouis" };
for (std::string const& each : data) {
if (std::find(each.begin(), each.end(), search_term) != std::string::npos) {
// contains it
}
}
That would be a good option I believe, if you really have to use a multimap then doing substrings of keys is kind of hard, since that's not what they're made for
Related
I'm using a very nice and simple std::vector<std::string> initializer which takes an input string and regex. It's similar to a basic split, just it works with regex Group1 matches:
static std::vector<std::string> match(const std::string& str, const std::regex& re) {
return { std::sregex_token_iterator(str.begin(), str.end(), re, 1), std::sregex_token_iterator() };
}
Construction of a vector is done like below:
std::string input = "aaa(item0,param0);bbb(item1,param1);cc(item2,param2);";
std::vector<std::string> myVector = match(input, std::regex(R"(\(([^,]*),)"));
This results a vector containing item0,item1,item2 extracted from an input string with regex:
Now my match function uses the first group results of the regex and (I believe) utilizes the vector's intialization form of:
std::vector<std::string> myVector = { ... };
I'd like to create a similar match function to construct a std::map<std::string,std::string>. Map also has the above initializator:
std::map<std::string,std::string> myMap = { {...}, {...} };
My idea is to modify the regex to create more group results:
And I would like to modify the above match function to create a nice map for me with the modified regex (\(([^,]*),([^)]*)), resulting the same as this:
std::map<std::string,std::string> myMap = { {"item0", "param0"}, {"item1", "param "}, {"item2", "param2"}, };
What I've tried?
static std::map<std::string, std::string> match(const std::string& str, const std::regex& re) {
return { std::sregex_token_iterator(str.begin(), str.end(), re, {1,2}), std::sregex_token_iterator() };
}
This one (in case of a vector) would put both Group1 and Group2 results into the vector. But it can not initialize a map.
How can I still do that easily (Is it not possible with sregex_token_iterator)?
I don't know what 'easily' does mean exactly, so here comes simple solution:
#include <iostream>
#include <regex>
#include <vector>
static std::map<std::string, std::string> match(const std::string& str, const std::regex& re) {
std::map<std::string, std::string> retVal;
auto token = std::sregex_token_iterator(str.begin(), str.end(), re, {1,2});
for (auto it=token++, jt=token; it != std::sregex_token_iterator(); ++it, jt = it++)
retVal.emplace(*it,*jt);
return retVal;
}
int main() {
std::string input = "aaa(item0,param0);bbb(item1,param1);cc(item2,param2);";
auto myVector = match(input, std::regex(R"(\(([^,]*),([^)]*))"));
for (const auto& item : myVector)
std::cout<<item.first<<'\t'<<item.second<<std::endl;
}
You can also could try to use boost and homemade generic algorithm.
This is basically what I want to do:
bool special_compare(const string& s1, const string& s2)
{
// match with wild card
}
std::vector<string> strings;
strings.push_back("Hello");
strings.push_back("World");
// I want this to find "Hello"
find(strings.begin(), strings.end(), "hell*", special_compare);
// And I want this to find "World"
find(strings.begin(), strings.end(), "**rld", special_compare);
But std::find doesn't work like that unfortunately. So using only the STL, how can I do something like this?
Based on your comments, you're probably looking for this:
struct special_compare : public std::unary_function<std::string, bool>
{
explicit special_compare(const std::string &baseline) : baseline(baseline) {}
bool operator() (const std::string &arg)
{ return somehow_compare(arg, baseline); }
std::string baseline;
}
std::find_if(strings.begin(), strings.end(), special_compare("hell*"));
The function you need to use is this : std::find_if, because std::find doesn't take compare function.
But then std::find_if doesn't take value. You're trying to pass value and compare both, which is confusing me. Anyway, look at the documentation. See the difference of the usage:
auto it1 = std::find(strings.begin(), strings.end(), "hell*");
auto it2 = std::find_if(strings.begin(), strings.end(), special_compare);
Hope that helps.
You'll need std::find_if(), which is awkward to use, unless you're on a C++11 compiler. Because then, you don't need to hardcode the value to search for in some comparator function or implement a functor object, but can do it in a lambda expression:
vector<string> strings;
strings.push_back("Hello");
strings.push_back("World");
find_if(strings.begin(), strings.end(), [](const string& s) {
return matches_wildcard(s, "hell*");
});
Then you write a matches_wildcard() somewhere.
Since nobody has mentioned std::bind yet, I'll propose this one
#include <functional>
bool special_compare(const std::string& s, const std::string& pattern)
{
// match with wild card
}
std::vector<std::string> strings;
auto i = find_if(strings.begin(), strings.end(), std::bind(special_compare, std::placeholders::_1, "hell*"));
With C++11 lambdas:
auto found = find_if(strings.begin(), strings.end(), [] (const std::string& s) {
return /* you can use "hell*" here! */;
});
If you can't use C++11 lambdas, you can just make a function object yourself. Make a type and overload operator ().
I wanted to have an example with custom class having custom find logic but didn't find any answer like that. So I wrote this answer which uses custom comparator function (C++11) to find an object.
class Student {
private:
long long m_id;
// private fields
public:
long long getId() { return m_id; };
};
Now suppose, I want to find the student object whose m_id matches with a given id. I can write std::find_if like this:
// studentList is a vector array
long long x_id = 3; // local variable
auto itr = std::find_if(studentList.begin(), studentList.end(),
[x_id](Student& std_val)
{ return std_val.getId() == x_id; }
);
if(itr == studentList.end())
printf("nothing found");
So I have a set of pairs<string ,string>
And I want to use find() to search for a single string which would be in the "first" of the pair, then if I find that string in first I want to return second from that function.
My current attempt is..
myList::iterator i;
i = theList.find(make_pair(realName, "*"));
return i->second;
Is C++11 acceptable?
auto it = find_if(theList.begin(), theList.end(),
[&](const pair<string, string>& val) -> bool {
return val.first == realName;
});
return it->second;
Or in C++03, first define a functor:
struct MatchFirst
{
MatchFirst(const string& realName) : realName(realName) {}
bool operator()(const pair<string, string>& val) {
return val.first == realName;
}
const string& realName;
};
then call it like so:
myList::iterator it = find_if(a.begin(), a.end(), MatchFirst(realName));
return it->second;
This will only return the first match, but from your question, it looks like that's all you're expecting.
You can use std::set<std::pair<std::string, std::string> > for this but you will need a custom
comparison object for this because the pair's relational operator takes both elements for this. That said, it seems as if you actually should use a std::map<std::string, std::string> instead.
The definition of < for std::pair implements a lexicographical order and "" is the minimum element for strings. Combining this we get:
typedef std::pair<std::string, std::string> StringPair;
typedef std::set<StringPair> Set;
std::string const* find_first(Set const& s, std::string const& key) {
Set::const_iterator const it = s.lower_bound(std::make_pair(key, ""));
// Check that it actually points to a valid element whose key is of interest.
if (it == s.end() or it->first != key) { return 0; }
// Yata!
return &it->second;
}
The trick is using lower_bound appropriately.
Returns an iterator pointing to the first element which does not compare less than value.
If it returns end(), then it did not find anything interesting.
Otherwise, it->first >= key so we get rid of the > case (of no interest to us)
I would point out though that this only returns the first element of the range. If you are interested in all elements, try:
typedef std::pair<Set::const_iterator, Set::const_iterator> SetItPair;
SetItPair equal_range_first(Set const& s, std::string const& key) {
StringPair const p = std::make_pair(key, "");
return std::make_pair(s.lower_bound(p), s.upper_bound(p));
}
This will return the full range of nodes in s whose first element is equal to key. You then just have to iterate over this range:
for (Set::const_iterator it = range.first; it != range.second; ++it) {
// do something
}
And you don't even have to worry whether the return of lower_bound or upper_bound was end or not.
if lower_bound returns end(), then so does upper_bound, and the loop is skipped
if lower_bound points to a node for which it->first > key, then upper_bound will point to that same node, and the loop is skipped
That is the power of ranges: no need to make special checks, the ranges just end up empty when there is no match, and so the loop over them... is skipped in a single check.
What is the most efficient way of obtaining lists (as a vector) of the keys and values from an unordered_map?
For concreteness, suppose the map in question is a unordered_map<string, double>.
I'd then like to obtain the keys as a vector<string>, and the values as a vector<double>.
unordered_map<string, double> um;
vector<string> vs = um.enum_keys();
vector<double> vd = um.enum_values();
I can just iterate across the map and collect the result, but is there a more
efficient method? It would be nice to have a method that also works for regular map,
since I might switch to that.
Okay, here you go:
std::vector<Key> keys;
keys.reserve(map.size());
std::vector<Val> vals;
vals.reserve(map.size());
for(auto kv : map) {
keys.push_back(kv.first);
vals.push_back(kv.second);
}
Efficiency can probably be improved, but there it is. You're operating on two containers though, so there's not really any STL magic that can hide that fact.
As Louis said, this will work for any of the STL map or set containers.
Using C++-14 you could also do the following (edited to contain full source):
#include <algorithm>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
using namespace std;
typedef string Key;
typedef int Value;
auto key_selector = [](auto pair){return pair.first;};
auto value_selector = [](auto pair){return pair.second;};
int main(int argc, char** argv) {
// Create a test map
unordered_map<Key, Value> map;
map["Eight"] = 8;
map["Ten"] = 10;
map["Eleven"] = 11;
// Vectors to hold keys and values
vector<Key> keys(map.size());
vector<Value> values(map.size());
// This is the crucial bit: Transform map to list of keys (or values)
transform(map.begin(), map.end(), keys.begin(), key_selector);
transform(map.begin(), map.end(), values.begin(), value_selector);
// Make sure this worked: Print out vectors
for (Key key : keys) cout << "Key: " << key << endl;
for (Value value : values) cout << "Value: " << value << endl;
return 0;
}
I compiled this with the following command:
g++ keyval.cpp -std=c++14 -o keyval
Testing it printed the keys and values as expected.
In STL there is no built-in method to get all keys or values from a map.
There is no different to iterate a unordered map or regular map, the best way is to iterate it and collect key or value to a vector.
You can write a template function to iterate any kind of map.
Joining late, but thought this might be helpful to someone.
Two template functions making use of key_type and mapped_type.
namespace mapExt
{
template<typename myMap>
std::vector<typename myMap::key_type> Keys(const myMap& m)
{
std::vector<typename myMap::key_type> r;
r.reserve(m.size());
for (const auto&kvp : m)
{
r.push_back(kvp.first);
}
return r;
}
template<typename myMap>
std::vector<typename myMap::mapped_type> Values(const myMap& m)
{
std::vector<typename myMap::mapped_type> r;
r.reserve(m.size());
for (const auto&kvp : m)
{
r.push_back(kvp.second);
}
return r;
}
}
Usage:
std::map<long, char> mO;
std::unordered_map<long, char> mU;
// set up the maps
std::vector<long> kO = mapExt::Keys(mO);
std::vector<long> kU = mapExt::Keys(mU);
std::vector<char> vO = mapExt::Values(mO);
std::vector<char> vU = mapExt::Values(mU);
Similar to #Keith Layne answer, but reserve is done in one line;
And the for loop is using reference (instead of copying it by value each entry) and making it const. But if C++14 can be used then #Marius Renn answer should be better.
std::map<long, char> mO;
// populate the mO
std::vector<long> keys(mO.size());
std::vector<char> vals(mO.size());
for(const auto &kv : mO) {
keys.push_back(kv.first);
vals.push_back(kv.second);
}
I'm using this with the range-v3 library, it will be soon in the STL library too, with a little luck in c++23 (ranges::to definitely and views::key maybe).
std::unordered_map<std::string, int> map {{"one", 1}, {"two", 2}, {"three", 3}};
auto keys = map | ranges::views::keys | ranges::to<std::vector>();
All I want to do is to check whether an element exists in the vector or not, so I can deal with each case.
if ( item_present )
do_this();
else
do_that();
You can use std::find from <algorithm>:
#include <algorithm>
#include <vector>
vector<int> vec;
//can have other data types instead of int but must same datatype as item
std::find(vec.begin(), vec.end(), item) != vec.end()
This returns an iterator to the first element found. If not present, it returns an iterator to one-past-the-end. With your example:
#include <algorithm>
#include <vector>
if ( std::find(vec.begin(), vec.end(), item) != vec.end() )
do_this();
else
do_that();
As others have said, use the STL find or find_if functions. But if you are searching in very large vectors and this impacts performance, you may want to sort your vector and then use the binary_search, lower_bound, or upper_bound algorithms.
If your vector is not ordered, use the approach MSN suggested:
if(std::find(vector.begin(), vector.end(), item)!=vector.end()){
// Found the item
}
If your vector is ordered, use binary_search method Brian Neal suggested:
if(binary_search(vector.begin(), vector.end(), item)){
// Found the item
}
binary search yields O(log n) worst-case performance, which is way more efficient than the first approach. In order to use binary search, you may use qsort to sort the vector first to guarantee it is ordered.
Use find from the algorithm header of stl.I've illustrated its use with int type. You can use any type you like as long as you can compare for equality (overload == if you need to for your custom class).
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
typedef vector<int> IntContainer;
typedef IntContainer::iterator IntIterator;
IntContainer vw;
//...
// find 5
IntIterator i = find(vw.begin(), vw.end(), 5);
if (i != vw.end()) {
// found it
} else {
// doesn't exist
}
return 0;
}
In C++11 you can use any_of. For example if it is a vector<string> v; then:
if (any_of(v.begin(), v.end(), bind(equal_to<string>(), _1, item)))
do_this();
else
do_that();
Alternatively, use a lambda:
if (any_of(v.begin(), v.end(), [&](const std::string& elem) { return elem == item; }))
do_this();
else
do_that();
I use something like this...
#include <algorithm>
template <typename T>
const bool Contains( std::vector<T>& Vec, const T& Element )
{
if (std::find(Vec.begin(), Vec.end(), Element) != Vec.end())
return true;
return false;
}
if (Contains(vector,item))
blah
else
blah
...as that way it's actually clear and readable.
(Obviously you can reuse the template in multiple places).
Here's a function that will work for any Container:
template <class Container>
const bool contains(const Container& container, const typename Container::value_type& element)
{
return std::find(container.begin(), container.end(), element) != container.end();
}
Note that you can get away with 1 template parameter because you can extract the value_type from the Container. You need the typename because Container::value_type is a dependent name.
Bear in mind that, if you're going to be doing a lot of lookups, there are STL containers that are better for that. I don't know what your application is, but associative containers like std::map may be worth considering.
std::vector is the container of choice unless you have a reason for another, and lookups by value can be such a reason.
Use the STL find function.
Keep in mind that there is also a find_if function, which you can use if your search is more complex, i.e. if you're not just looking for an element, but, for example, want see if there is an element that fulfills a certain condition, for example, a string that starts with "abc". (find_if would give you an iterator that points to the first such element).
With boost you can use any_of_equal:
#include <boost/algorithm/cxx11/any_of.hpp>
bool item_present = boost::algorithm::any_of_equal(vector, element);
You can try this code:
#include <algorithm>
#include <vector>
// You can use class, struct or primitive data type for Item
struct Item {
//Some fields
};
typedef std::vector<Item> ItemVector;
typedef ItemVector::iterator ItemIterator;
//...
ItemVector vtItem;
//... (init data for vtItem)
Item itemToFind;
//...
ItemIterator itemItr;
itemItr = std::find(vtItem.begin(), vtItem.end(), itemToFind);
if (itemItr != vtItem.end()) {
// Item found
// doThis()
}
else {
// Item not found
// doThat()
}
From C++20, using ranges (#include <ranges>)
//SAMPLE DATA
std::vector<int> vecOfElements = { 2,4,6,8 };
//DO SOMETHING IF 8 IN VECTOR
if (std::ranges::find(vecOfElements, 8) != vecOfElements.end())
{
std::cout << "DO SOMETHING" << std::endl;
}
You can use the find function, found in the std namespace, ie std::find. You pass the std::find function the begin and end iterator from the vector you want to search, along with the element you're looking for and compare the resulting iterator to the end of the vector to see if they match or not.
std::find(vector.begin(), vector.end(), item) != vector.end()
You're also able to dereference that iterator and use it as normal, like any other iterator.
You can use count too.
It will return the number of items present in a vector.
int t=count(vec.begin(),vec.end(),item);
template <typename T> bool IsInVector(const T & what, const std::vector<T> & vec)
{
return std::find(vec.begin(),vec.end(),what)!=vec.end();
}
I've personally used templates of late to handle multiple types of containers at once rather than deal only with vectors. I found a similar example online (can't remember where) so credit goes to whoever I've pilfered this from. This particular pattern seems to handle raw arrays as well.
template <typename Container, typename T = typename std::decay<decltype(*std::begin(std::declval<Container>()))>::type>
bool contains(Container && c, T v)
{
return std::find(std::begin(c), std::end(c), v) != std::end(c);
}
(C++17 and above):
can use std::search also
This is also useful for searching sequence of elements.
#include <algorithm>
#include <iostream>
#include <vector>
template <typename Container>
bool search_vector(const Container& vec, const Container& searchvec)
{
return std::search(vec.begin(), vec.end(), searchvec.begin(), searchvec.end()) != vec.end();
}
int main()
{
std::vector<int> v = {2,4,6,8};
//THIS WORKS. SEARCHING ONLY ONE ELEMENT.
std::vector<int> searchVector1 = {2};
if(search_vector(v,searchVector1))
std::cout<<"searchVector1 found"<<std::endl;
else
std::cout<<"searchVector1 not found"<<std::endl;
//THIS WORKS, AS THE ELEMENTS ARE SEQUENTIAL.
std::vector<int> searchVector2 = {6,8};
if(search_vector(v,searchVector2))
std::cout<<"searchVector2 found"<<std::endl;
else
std::cout<<"searchVector2 not found"<<std::endl;
//THIS WILL NOT WORK, AS THE ELEMENTS ARE NOT SEQUENTIAL.
std::vector<int> searchVector3 = {8,6};
if(search_vector(v,searchVector3))
std::cout<<"searchVector3 found"<<std::endl;
else
std::cout<<"searchVector3 not found"<<std::endl;
}
Also there is flexibility of passing some search algorithms. Refer here.
https://en.cppreference.com/w/cpp/algorithm/search
If you wanna find a string in a vector:
struct isEqual
{
isEqual(const std::string& s): m_s(s)
{}
bool operator()(OIDV* l)
{
return l->oid == m_s;
}
std::string m_s;
};
struct OIDV
{
string oid;
//else
};
VecOidv::iterator itFind = find_if(vecOidv.begin(), vecOidv.end(), isEqual(szTmp));