Sorting an STL vector on two values - c++

How do I sort an STL vector based on two different comparison criterias? The default sort() function only takes a single sorter object.

You need to combine the two criteria into one.
Heres an example of how you'd sort a struct with a first and second field
based on the first field, then the second field.
#include <algorithm>
struct MyEntry {
int first;
int second;
};
bool compare_entry( const MyEntry & e1, const MyEntry & e2) {
if( e1.first != e2.first)
return (e1.first < e2.first);
return (e1.second < e2.second);
}
int main() {
std::vector<MyEntry> vec = get_some_entries();
std::sort( vec.begin(), vec.end(), compare_entry );
}
NOTE: implementation of compare_entry updated to use code from Nawaz.

Related

Getting error while sorting a map by value [duplicate]

I was trying to make a map sort by value using a custom comparator but I couldn't figure out why I kept getting the error of "no matching call to compareByVal"
Here's what I had in my main.cpp:
#include <map>
#include <iostream>
struct compareByVal {
bool operator[](const std::pair<int,int> & a, const std::pair<int,int> & b)
return a.second < b.second;
}
int main() {
std::map<int,int,compareByVal> hash;
hash[1] = 5;
hash[2] = 2;
hash[3] = 10;
std::cout << hash.begin()->first << std::endl;
}
The first, simple problem is
struct compareByVal {
bool operator[](const std::pair<int,int> & a, const std::pair<int,int> & b)
return a.second < b.second;
}
should be
struct compareByVal {
bool operator()(const std::pair<int,int> & a, const std::pair<int,int> & b) const {
return a.second < b.second;
}
};
The second, serious problem is the signature of the compare is wrong. It should be
struct compareByVal {
bool operator()(const int leftKey, const int rightKey) const;
}
You can't access the value in the compare function. There is no (simple) way to sort a map by value.
Simply put, you cannot. Not sure which compiler you're using, but clang and gcc both give useful messages. with context.
clang:
static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},
gcc:
if (__i == end() || key_comp()(__k, (*__i).first))
You can see that clang and gcc are both calling the compare method with only they key, and not a value. This is simply how maps work.
If you want to sort by value, you would have to create your own custom map, or, more realistically, use the value as the key instead. Creating your own map to achieve this would be more difficult than you'd think, since it would have to sort after any value is modified.
If you want to sort a std::map by its value, then you are using the wrong container. std::map is sorted by the keys by definition.
You can wrap key and value:
struct foo {
int key;
int value;
};
and then use a std::set<foo> that uses a comparator that only compares foo::value.
Well, first, the reason you're getting the error: "no matching call to compareByVal" is because map's comparator works only with the keys. So the comparator should like:
struct compareByVal {
template <typename T>
bool operator()(const T& a, const T& b) const
return a < b;
}
Coming on to what you want to achieve, I see two ways of doing so:
Copy all the elements of the map to a std::vector and sort that:
std::vector<std::pair<int,int> > v(hash.begin(), hash.end());
std::sort(v.begin(), v.end(), [](const auto& a, const auto& b) { return a.second < b.second; });
Copy all the elements of the map to another map with keys as values and values as keys. If values of your map are not unique, you can use a std::multimap instead.
This may be an X-Y issue.
If you need to sort by both key and value, then a single std::map may not be the most efficient choice.
In database theory, all the data would be placed into a single table. An index table would be created describing the access or sorting method. Data that needs to be sorted in more than one method would have multiple index tables.
In C++, the core table would be a std::vector. The indices would be std::map<key1, vector_index>, std::map<key2, vector_index>, where vector_index is the index of the item in the core table.
Example:
struct Record
{
int age;
std::string name;
};
// Core table
std::vector<Record> database;
// Index by age
std::map<int, unsigned int> age_index_table;
// Index by name
std::map<std::string, unsigned int> name_index_table;
// Fetching by age:
unsigned int database_index = age_index_table[42];
Record r = database[database_index];
// Fetching by name:
unsigned int database_index = name_index_table["Harry Potter"];
Record r = database[database_index];
You can learn more by searching the internet for "database index tables c++".
If it looks like a database and smells like a database ...

How to make a map sort by value C++

I was trying to make a map sort by value using a custom comparator but I couldn't figure out why I kept getting the error of "no matching call to compareByVal"
Here's what I had in my main.cpp:
#include <map>
#include <iostream>
struct compareByVal {
bool operator[](const std::pair<int,int> & a, const std::pair<int,int> & b)
return a.second < b.second;
}
int main() {
std::map<int,int,compareByVal> hash;
hash[1] = 5;
hash[2] = 2;
hash[3] = 10;
std::cout << hash.begin()->first << std::endl;
}
The first, simple problem is
struct compareByVal {
bool operator[](const std::pair<int,int> & a, const std::pair<int,int> & b)
return a.second < b.second;
}
should be
struct compareByVal {
bool operator()(const std::pair<int,int> & a, const std::pair<int,int> & b) const {
return a.second < b.second;
}
};
The second, serious problem is the signature of the compare is wrong. It should be
struct compareByVal {
bool operator()(const int leftKey, const int rightKey) const;
}
You can't access the value in the compare function. There is no (simple) way to sort a map by value.
Simply put, you cannot. Not sure which compiler you're using, but clang and gcc both give useful messages. with context.
clang:
static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},
gcc:
if (__i == end() || key_comp()(__k, (*__i).first))
You can see that clang and gcc are both calling the compare method with only they key, and not a value. This is simply how maps work.
If you want to sort by value, you would have to create your own custom map, or, more realistically, use the value as the key instead. Creating your own map to achieve this would be more difficult than you'd think, since it would have to sort after any value is modified.
If you want to sort a std::map by its value, then you are using the wrong container. std::map is sorted by the keys by definition.
You can wrap key and value:
struct foo {
int key;
int value;
};
and then use a std::set<foo> that uses a comparator that only compares foo::value.
Well, first, the reason you're getting the error: "no matching call to compareByVal" is because map's comparator works only with the keys. So the comparator should like:
struct compareByVal {
template <typename T>
bool operator()(const T& a, const T& b) const
return a < b;
}
Coming on to what you want to achieve, I see two ways of doing so:
Copy all the elements of the map to a std::vector and sort that:
std::vector<std::pair<int,int> > v(hash.begin(), hash.end());
std::sort(v.begin(), v.end(), [](const auto& a, const auto& b) { return a.second < b.second; });
Copy all the elements of the map to another map with keys as values and values as keys. If values of your map are not unique, you can use a std::multimap instead.
This may be an X-Y issue.
If you need to sort by both key and value, then a single std::map may not be the most efficient choice.
In database theory, all the data would be placed into a single table. An index table would be created describing the access or sorting method. Data that needs to be sorted in more than one method would have multiple index tables.
In C++, the core table would be a std::vector. The indices would be std::map<key1, vector_index>, std::map<key2, vector_index>, where vector_index is the index of the item in the core table.
Example:
struct Record
{
int age;
std::string name;
};
// Core table
std::vector<Record> database;
// Index by age
std::map<int, unsigned int> age_index_table;
// Index by name
std::map<std::string, unsigned int> name_index_table;
// Fetching by age:
unsigned int database_index = age_index_table[42];
Record r = database[database_index];
// Fetching by name:
unsigned int database_index = name_index_table["Harry Potter"];
Record r = database[database_index];
You can learn more by searching the internet for "database index tables c++".
If it looks like a database and smells like a database ...

How to get corresponding pair regarding to a given value from a sorted vector of pairs in C++

I need to get the corresponding pair regarding a given value from a sorted vector of pair container. using 'binary search'. how to do that?
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<pair<int,int>> values;
values.push_back(make_pair(6,9));
values.push_back(make_pair(2,5));
values.push_back(make_pair(12,15));
sort(values.begin(), values.end()); // sorting the vector according to 'first' value
/*then the vector be like [(2,5), (6,9), (12,15)]*/
/*assume : no duplicate or overlapped pairs*/
/* I need to implement a function to get the corresponding pair related to a given value using 'binary search' method*/
/*eg:
if given_value = 7, then function should be return (6,7)
if given_value = 10, then function shold be return NULL
how i do this. is there a predefined way to do this ? */
}
Use std::lower_bound with a custom comparator:
std::optional<std::pair<int,int>> find(const std::vector<std::pair<int, int>>& vec, int searched) {
auto it = std::lower_bound(cbegin(vec), cend(vec), std::pair<int, int>{searched, 0}, [](const auto& a, const auto& b) {return a.first < b.first;});
if(it == cend(vec) || searched < it->first) { // nothing found, return nullopt
return {};
// alt: return cend(vec);
}
return {*it}; // return value wrapped in optional
// alt: return it;
}
Note: This requires C++17 for the optional. You can also just return the iterator if you found something an do the comparison at the caller (see alternatives in the code above).

How to compare vector with array in specific way in efficient way?

I would like to compare a vector with an array assuming that elements are in different order.
I have got a struct like below (this struct hasn't got "operator==" and "operator<" -> so I can't use sort):
struct A
{
int index;
A(int p_i) : index(p_i) {}
};
The size of the vector and the array is the same:
std::vector<A> l_v = {A(1), A(2), A(3)};
A l_a[3] = {A(3), A(1), A(2)};
I am looking for some function from std like below "some_function_X" which can find element in specific way using lambda, or function which can compare only specific field like "lhs.index == rhs.index" -> by specific field in class without "operator==" and "operator>" etc.
bool checkIfTheSame(const std::vector<A>& l_v, const A& l_a)
{
for(usigned int i=0; i< 3; ++i)
{
if(!std::some_function_X(l_v.begin(), l_v.end(), l_a,
[](const A& lhs, const A& rhs){
return lhs.index == rhs.index;
})) return false;
}
return true;
}
Thanks.
this struct hasn't got "operator==" and "operator<" -> so I can't use sort
Firstly, only operator< is required. Secondly, it doesn't have to be defined as a member function. The following free function works with std::less (which is what std::sort uses if you don't pass a functor for comparison).
bool operator<(const A& a, const A& b) {...}
Or, you can use a custom comparison functor instead of std::less.
Sorting the array and the vector and then comparing should have better asymptotic runtime complexity than trivially iterating one and checking if the element exists in the other using linear search.
Another idea would be to sort only one of them, iterate the unordered and test the existence in sorted container with binary search. That has same asymptotic complexity as sorting both.
If you can't modify either container, then you can make a copy for sorting. That costs some memory, but is still asymptotically faster than the trivial approach. std::partial_sort_copy can save you a few copies by sorting directly to the copy.
look here : http://www.cplusplus.com/reference/algorithm/
find with lambda expression find if
function which can compare only specific field like "lhs.index == rhs.index" -> by specific field in class without "operator==" and "operator>" etc.
to check if your 2 containers contains same datas with a predicate : equal with a custom predicate
if you just want to check if your container container a specific value, have a look here : How to find if an item is present in a std::vector?
I'm not sure I fully understand what you want but I'll take a guess that you want to check whether two arrays (which have different container types) are equivalent, that is for each item in array A there is a corresponding item somewhere in array B for which some predicate returns true.
You want to do this on two arrays that are not only of different types, but also possibly in a different order.
This is one way to do it:
struct Foo
{
Foo(int i) : _index { i } {}
int index() const {
return _index;
}
private:
int _index;
};
// params:
// first1, last1 bound the first array
// first2, last2 bound the second array
// pred is a predicate that checks for equivalents
// order is a predicate that performs weak ordering
template<class Iter1, class Iter2, class Pred, class Order>
bool checkIfTheSame(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2, Pred pred, Order order)
{
const auto len1 = last1 - first1;
const auto len2 = last2 - first2;
if (len1 != len2)
return false;
using namespace std;
using item1_type = typename std::iterator_traits<Iter1>::value_type;
using item2_type = typename std::iterator_traits<Iter2>::value_type;
std::vector<std::reference_wrapper<item1_type>> refs1 { first1, last1 };
std::sort(begin(refs1), end(refs1), order);
std::vector<std::reference_wrapper<item2_type>> refs2 { first2, last2 };
std::sort(begin(refs2), end(refs2), order);
for (size_t i = 0 ; i < len1 ; ++i) {
if (!pred(refs1[i], refs2[i]))
return false;
}
return true;
}
simple example:
using namespace std;
bool same_indexes(const Foo& f1, const Foo& f2) {
return f1.index() == f2.index();
}
bool sort_indexes(const Foo& f1, const Foo& f2) {
return f1.index() < f2.index();
}
int main(int argc, const char * argv[])
{
vector<Foo> x { 1, 2, 3 };
Foo y[] = { 3, 2, 1 };
cout << checkIfTheSame(begin(x), end(x), begin(y), end(y), same_indexes, sort_indexes) << endl;
return 0;
}
If you do not have any way to sort the array or the vector, but you are only able to know if two elements are equal, the only method is an exhaustive search.

Erase-remove idiom for deleting in a nested container? (deleting outer ones; C++ STL)

when i'm deleting from a non-nested container like a vector, i'm doing something like:
struct is_to_remove
{
is_to_remove(dynamic_bitset<>& x) : x(x) {}
const bool operator()(unsigned int id)
{
return x[id];
}
private:
dynamic_bitset<> x;
};
inline static void remove_elements_in_vector(vector<unsigned int>& vec, boost::dynamic_bitset<>& to_remove)
{
// use the erase-remove idiom to remove all elements marked in bitset
vec.erase( remove_if(vec.begin(), vec.end(), is_to_remove(to_remove)), vec.end() );
}
That is the so called erase-remove idiom.
Now, i have a second data strucurevector<vector<unsigned int> > or deque<vector<unsigned int> >, where i want to delete the outer container elements (which is itself a container of the inner type) according to a bitset.
Is it possible to use the erase-remove idiom on this nested container types?
If it is, how is it possible?
Are there restrictions? (like: vec of vec is possible, but not deque of vec)?
My first and naive approach was the following. I assumed that remove_if is iterating sequentially and in order over the elements and deciding one after the other. Is that a wrong assumption?
struct is_to_remove_new
{
is_to_remove_new(dynamic_bitset<>& x, unsigned int index) : x(x), index(index) {}
const bool operator()(vector<unsigned int> & vec)
{
return x[index++];
}
private:
dynamic_bitset<> x;
unsigned int index;
};
inline static void remove_elements_in_vectorvector(vector<vector<unsigned int> >& vec, boost::dynamic_bitset<>& to_remove)
{
// use the erase-remove idiom to remove all elements marked in bitset
vec.erase( remove_if(vec.begin(), vec.end(), is_to_remove_new(to_remove, 0)), vec.end() );
}
The result is wrong, therefore i'm looking for a correct solution here. I suppose i assumed some things, which aren't guaranteed. For me, the base question is the following: How to get the identity of the inner container for checking if it is to remove..
My naive approach posted above just counts and assumes a sequential processing.
Thanks for your help.
Sascha
Update and Warning
For a vector o vectors, Stas solution is working great. But i think this solution will not work for a deque of vectors because a deque isn't saved in a contiguous way. This means, that the calculation of the index in the functor fails.
Can anyone verify that?
It doesn't matter which elements are in the vector. If you define which of them should be removed, they will be removed.
As you said, the question is how to identify an element in a vector. The most obvious answer is by its index [0; vector_size - 1].
Thanks to a vector, you can easily get an element index, by the element itself.
std::vector<std::string> vec;
vec.push_back("zero");
vec.push_back("one");
vec.push_back("two");
std::string& elem = vec[2];
int index = std::distance(&vec[0], &elem); // get element index by element itself
// index == 2
So, you can easily identify vector elements by indices inside remove_if algorithm predicate. Take a look at the following example. It is pretty silly and use hard-coded std::bitset<6>, but this is just an illustration:
#include <vector>
#include <string>
#include <bitset>
struct ToRemove
{
ToRemove(std::vector<std::string>& vec, const std::string& mask)
: m_vec(vec)
, m_mask(mask)
{}
bool operator()(std::string& obj)
{
const int index = std::distance(&m_vec[0], &obj);
return m_mask[index];
}
std::vector<std::string>& m_vec;
std::bitset<6> m_mask;
};
Usage
int main (int argc, char const *argv[])
{
std::vector<std::string> vec;
vec.push_back("zero");
vec.push_back("one");
vec.push_back("two");
vec.push_back("three");
vec.push_back("four");
vec.push_back("five");
std::string mask = ("010011"); // Remove "zero", "one" and "four"
vec.erase(remove_if(vec.begin(), vec.end(), ToRemove(vec, mask)), vec.end());
for (unsigned i = 0; i < vec.size(); ++i)
{
std::cout << vec[i] << " ";
}
std::cout << std::endl;
return 0;
}
Result
two three five