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 the a template class with template parameters:
K
: is a for whichless
is implemented (anint
for example)T
: is the item type which should be able to fit in a standard collectionpredicateFilter
: follow the same rules as UnaryPredicate for std::copy_ifProjection
: follow the same rules as UnaryOperation for std::transform
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)
}
};
So 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 improved the performance, clarity or design? Are there better standard collection than the one I used?