Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

find an element in std::vector of std::any

Tags:

c++

c++17

stdany

I want to check whether an element exists in the vector or not. I know the below piece of code will check it.

#include <algorithm>

if ( std::find(vector.begin(), vector.end(), item) != vector.end() )
   std::cout << "found";
else
   std::cout << "not found";

But I have the vector of any type. i.e. std::vector<std::any> I am pushing elements into vector like this.

std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);

So I need to find whether string "A" present in the vector or not. Can std::find help here?

As of now I am using below piece of code to do this

bool isItemPresentInAnyVector(std::vector<std::any> items, std::any item)
{
    for (const auto& it : items)
    {
        if (it.type() == typeid(std::string) && item.type() == typeid(std::string))
        {
            std::string strVecItem = std::any_cast<std::string>(it);
            std::string strItem = std::any_cast<std::string>(item);

            if (strVecItem.compare(strItem) == 0)
                return true;
        }
        else if (it.type() == typeid(int) && item.type() == typeid(int))
        {
            int iVecItem = std::any_cast<int>(it);
            int iItem = std::any_cast<int>(item);

            if (iVecItem == iItem)
                return true;
        }
        else if (it.type() == typeid(float) && item.type() == typeid(float))
        {
            float fVecItem = std::any_cast<float>(it);
            float fItem = std::any_cast<float>(item);

            if (fVecItem == fItem)
                return true;
        }
    }

    return false;
}
like image 790
Arun Avatar asked Mar 08 '19 06:03

Arun


2 Answers

This should work good I guess:

#include <vector>
#include <string>
#include <any>
#include <algorithm>
#include <iostream>

int main(){
    std::vector<std::any> temp;
    temp.emplace_back(std::string("A"));
    temp.emplace_back(10);
    temp.emplace_back(3.14f);

    int i = 10;//you can use any type for i variable and it should work fine
    //std::string i = "A"; 
    auto found = std::find_if(temp.begin(), temp.end(), [i](const auto &a){
        return typeid(i) == a.type() && std::any_cast<decltype(i)>(a) == i;
    } );

    std::cout << std::any_cast<decltype(i)>(*found);
}

Or to make the code a bit more generic and reusable:

#include <vector>
#include <string>
#include <any>
#include <algorithm>
#include <iostream>


auto any_compare = [](const auto &i){
    return [i] (const auto &val){
        return typeid(i) == val.type() && std::any_cast<decltype(i)>(val) == i;
    };
};

int main(){
    std::vector<std::any> temp;
    temp.emplace_back(std::string("A"));
    temp.emplace_back(10);
    temp.emplace_back(3.14f);

    //int i = 10;
    std::string i = "A";
    auto found = std::find_if(temp.begin(), temp.end(), any_compare(i));

    std::cout << std::any_cast<decltype(i)>(*found);
}

Live demo

Important note: this is guaranteed to work only within single translation unit due to stadard requirements on std::any type (for example same types don't need to have same type identifier in different translation units)

like image 139
bartop Avatar answered Oct 03 '22 04:10

bartop


Comparison with typeId() should be avoided since it's dependent from translation unit.

A much safer approach can be used with any_cast of pointers:

template<typename T>
std::optional<T> find(const std::vector<std::any>& v)
{
   for(auto&& e : v){
      if(auto ptr = std::any_cast<T>(&e)){
         return *ptr;
      }
   }

   return std::nullopt;
} 

Find first element with the given type, or nullopt if it's not found.

If we want to find all element with a specific instead:

template<typename T>
std::vector<T> findAll(const std::vector<std::any>& v)
{
   std::vector<T> out;
   for(auto&& e : v){
      if(auto ptr = std::any_cast<T>(&e)){
         out.push_back(*ptr);
      }
   }

   return out;
}

Usage:

int main()
{
    std::vector<std::any> temp;
    temp.emplace_back(std::string("A"));
    temp.emplace_back(10);
    temp.emplace_back(3.14f);
    temp.emplace_back(12);
    temp.emplace_back(std::string("B"));
    
    auto outInt = findAll<int>(temp);
    
    std::cout << "out int: " << outInt.size() << std::endl;
    for(auto&& out : outInt)
        std::cout << out << std::endl;
        
    auto outString = findAll<std::string>(temp);
    
    std::cout << "out string: " << outString.size() << std::endl;
    for(auto&& out : outString)
        std::cout << out << std::endl;
        
    auto singleInt = find<int>(temp);
    if(singleInt)
         std::cout << "first int " << *singleInt << std::endl;
         
    auto singleBool = find<bool>(temp);
    if(!singleBool)
         std::cout << "ok: bool not found" << std::endl;
}

LIVE DEMO

like image 44
Moia Avatar answered Oct 03 '22 04:10

Moia