1
\$\begingroup\$

I want to simulate, in C++, the fact that in a form with drop down (or combo boxes), you can filter by selecting items and then projecting on a given set of properties for these item.

In a SQL-like query it could be:

select age, name 
from (select * from table t where isoncodereview = true)
where country = "US";

I have come up with a template class with template parameters:

The complete declaration of the class is:

template<class K, class T> class IterativeSettingFilter
{
public:

    IterativeSettingFilter(){}

    std::vector<T>& originalSet()
    {
        return _origset;
    }

    const std::vector<T>& currentSet() const
    {
        return history.empty() ? _origset : history.back().second;
    }

    void reset()
    {
        history.clear();
        indexByKey.clear();
    }

    template <class predicateFilter>
    bool applyCondition(const K& id, const predicateFilter& customfilter)
    {
        std::vector<T> nextSet;
        std::copy_if(currentSet().begin(), currentSet().end(), std::back_inserter(nextSet), customfilter);

        indexByKey.insert( std::make_pair(id, history.size()) );
        history.push_back(std::make_pair(id, nextSet));

        return true;
    }

    std::vector<T> setAt(const K& id) const
    {
        auto it = indexByKey.find(id);
        if(it == indexByKey.end())
            return std::vector<T>();

        size_t index = it->second;
        return history[index];
    }

    bool rollBackBeforeCondition(const K& id)
    {
        if(id == defaultid)
        {
            reset();
            return true;
        }
        auto it = indexByKey.find(id);
        if(it == indexByKey.end())
            return false;

        size_t index = it->second;
        while( !history.empty() && history.size() > index)//size might be linear with list before c++11
        {
            indexByKey.erase(history.back().first);
            history.pop_back();
        }
        return true;
    }

    template <class E, class Projection>
    std::set<E> project(const Projection& projection) const
    {
        std::set<E> projected;
        std::transform(currentSet().begin(), currentSet().end(), std::inserter(projected, projected.end()), projection);
        return projected;
    }

    template <class E, class Projection>
    std::set<E> project(const K& id, const Projection& projection) const
    {
        std::set<E> projected;
        const std::vector<T>& selectedVect = correspondingSet(id); 
        std::transform(selectedVect.begin(), selectedVect.end(), std::inserter(projected, projected.end()), projection);
        return projected;
    }

    K predecessor(const K& id) const
    {
        auto iter = indexByKey.find(id);
        if( iter != indexByKey.end() )
        {
            size_t pos = iter->second;
            return pos == 0 ? defaultid : history.at(pos - 1).first;
        }
        else
        {
            return history.empty() ? defaultid : history.back().first;
        }
    }

private:

    const std::vector<T>& correspondingSet(const K& id) const
    {
        if(id == defaultid )
        {
            return  _origset;
        }
        else
        {
            auto iter = indexByKey.find(id);
            if( iter != indexByKey.end() )
            {
                size_t pos = iter->second;
                return history.at(pos).second;
            }
            else
            {
                return currentSet();
            }
        }
    }

private:
    K defaultid;
    std::vector<T> _origset;

    std::vector< std::pair< K, std::vector<T> > > history;
    std::map< K, size_t > indexByKey;

public:
    // http://en.cppreference.com/w/cpp/concept/Swappable
    void swap(IterativeSettingFilter& other) 
    {
        swap(defaultid, other.defaultid);
        swap(_origset,  other._origset);
        swap(history,   other.history);
        swap(indexByKey,other.indexByKey)
    }
};

You can apply as many filters to obtain an ordered list of generations. For any generation, you can project on a given property to obtain a unique set. Can I improve the performance, clarity or design? Are there better standard collections than the one I've used?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.