C ++ There is a vector of tuples of type

std::vector <tuple< ULONG, ULONG, ULONG, time_t, wstring, int >> drv; std::vector <drvToTuple> drv; 

first sort it

 bool sortbyPath(const tuple<ULONG, ULONG, ULONG, time_t, wstring, int>& a, const tuple<ULONG, ULONG, ULONG, time_t, wstring, int>& b) { return (get<4>(a) < get<4>(b)); } sort(drv.begin(), drv.end(), sortbyPath); 

Tell me how to remove unique values ​​based on 4 field (wstring) of the tuple?

 2056, 2328, 94, 1545877351, L"Sasha", 15 2057, 2328, 94, 1545877351, L"Masha", 15 2057, 2328, 94, 1545877353, L"Dasha", 15 2058, 2328, 94, 1545877353, L"Sasha", 15 2059, 2328, 94, 1545877354, L"Misha", 15 2059, 2328, 94, 1545877354, L"Misha", 15 

in the end it should remain

 2056, 2328, 94, 1545877351, L"Sasha", 15 2057, 2328, 94, 1545877351, L"Masha", 15 2057, 2328, 94, 1545877353, L"Dasha", 15 2059, 2328, 94, 1545877354, L"Misha", 15 
  • You wanted to say how to leave unique values ​​and remove duplicate values? For this there is a standard algorithm std :: unique - Vlad from Moscow
  • Why did you start with a story about sorting, if nothing is sorted in your examples? Is preserving the original order of the elements a task requirement? - AnT
  • Well, at least because std :: unique removes neighboring elements, and to remove all duplicates with it, you must first sort it. This is followed by the question I asked above. The original order is not important. - Sergey Romanov

2 answers 2

You can use a combination of the erase vector erase with the standard std::unique algorithm. (If you want to place the result in another vector or container, then you can use the algorithm std::unique_copy ).

For example,

 drv.erase( std::unique( std::begin( drv ), std::end( drv ), []( const auto &a, const auto &b ) { return std::get<4>( a ) == std::get<4>( b ); } ), std::end( drv ) ); 

Below is a demonstration program.

 #include <iostream> #include <iomanip> #include <string> #include <tuple> #include <vector> #include <iterator> #include <algorithm> #include <ctime> typedef unsigned long ULONG; int main() { std::vector<std::tuple<ULONG, ULONG, ULONG, time_t, std::wstring, int>> drv = { { 056, 2328, 94, 1545877351, L"Sasha", 15 }, { 2057, 2328, 94, 1545877351, L"Masha", 15 }, { 2057, 2328, 94, 1545877353, L"Dasha", 15 }, { 2058, 2328, 94, 1545877353, L"Sasha", 15 }, { 2059, 2328, 94, 1545877354, L"Misha", 15 }, { 2059, 2328, 94, 1545877354, L"Misha", 15 } }; for ( const auto &item : drv ) { std::wcout << std::setw( 4 ) << std::get<0>( item ) << ", " << std::setw( 4 ) << std::get<1>( item ) << ", " << std::setw( 2 ) << std::get<2>( item ) << ", " << std::get<3>( item ) << ". " << std::get<4>( item ) << ", " << std::get<5>( item ) << '\n'; } std::wcout << '\n'; std::sort( std::begin( drv ), std::end( drv ), []( const auto &a, const auto &b ) { return std::get<4>( a ) < std::get<4>( b ); } ); drv.erase( std::unique( std::begin( drv ), std::end( drv ), []( const auto &a, const auto &b ) { return std::get<4>( a ) == std::get<4>( b ); } ), std::end( drv ) ); for ( const auto &item : drv ) { std::wcout << std::setw( 4 ) << std::get<0>( item ) << ", " << std::setw( 4 ) << std::get<1>( item ) << ", " << std::setw( 2 ) << std::get<2>( item ) << ", " << std::get<3>( item ) << ". " << std::get<4>( item ) << ", " << std::get<5>( item ) << '\n'; } return 0; } 

Its output to the console:

  46, 2328, 94, 1545877351. Sasha, 15 2057, 2328, 94, 1545877351. Masha, 15 2057, 2328, 94, 1545877353. Dasha, 15 2058, 2328, 94, 1545877353. Sasha, 15 2059, 2328, 94, 1545877354. Misha, 15 2059, 2328, 94, 1545877354. Misha, 15 2057, 2328, 94, 1545877353. Dasha, 15 2057, 2328, 94, 1545877351. Masha, 15 2059, 2328, 94, 1545877354. Misha, 15 46, 2328, 94, 1545877351. Sasha, 15 

On the other hand, perhaps you should immediately choose another container, such as std::set or std::unordered_set .

Below is a demo program that shows how you can select only the unique elements of the source vector in the set std::set without changing the vector itself.

 #include <iostream> #include <iomanip> #include <string> #include <tuple> #include <vector> #include <set> #include <iterator> #include <algorithm> #include <ctime> typedef unsigned long ULONG; int main() { std::vector<std::tuple<ULONG, ULONG, ULONG, time_t, std::wstring, int>> drv = { { 056, 2328, 94, 1545877351, L"Sasha", 15 }, { 2057, 2328, 94, 1545877351, L"Masha", 15 }, { 2057, 2328, 94, 1545877353, L"Dasha", 15 }, { 2058, 2328, 94, 1545877353, L"Sasha", 15 }, { 2059, 2328, 94, 1545877354, L"Misha", 15 }, { 2059, 2328, 94, 1545877354, L"Misha", 15 } }; for ( const auto &item : drv ) { std::wcout << std::setw( 4 ) << std::get<0>( item ) << ", " << std::setw( 4 ) << std::get<1>( item ) << ", " << std::setw( 2 ) << std::get<2>( item ) << ", " << std::get<3>( item ) << ". " << std::get<4>( item ) << ", " << std::get<5>( item ) << '\n'; } std::wcout << '\n'; auto cmp = []( const auto &a, const auto &b ) { return std::get<4>( a ) < std::get<4>( b ); }; std::set<std::tuple<ULONG, ULONG, ULONG, time_t, std::wstring, int>, decltype( cmp )> tuple_set( cmp ); tuple_set.insert( std::begin( drv ), std::end( drv ) ); for ( const auto &item : tuple_set ) { std::wcout << std::setw( 4 ) << std::get<0>( item ) << ", " << std::setw( 4 ) << std::get<1>( item ) << ", " << std::setw( 2 ) << std::get<2>( item ) << ", " << std::get<3>( item ) << ". " << std::get<4>( item ) << ", " << std::get<5>( item ) << '\n'; } std::wcout << '\n'; return 0; } 

The output of the program to the console:

  46, 2328, 94, 1545877351. Sasha, 15 2057, 2328, 94, 1545877351. Masha, 15 2057, 2328, 94, 1545877353. Dasha, 15 2058, 2328, 94, 1545877353. Sasha, 15 2059, 2328, 94, 1545877354. Misha, 15 2059, 2328, 94, 1545877354. Misha, 15 2057, 2328, 94, 1545877353. Dasha, 15 2057, 2328, 94, 1545877351. Masha, 15 2059, 2328, 94, 1545877354. Misha, 15 46, 2328, 94, 1545877351. Sasha, 15 

    Use std::unique() and binary predicate using your sortbyPath() function:

     template< class ExecutionPolicy, class ForwardIt, class BinaryPredicate > ForwardIt unique( ExecutionPolicy&& policy, ForwardIt first, ForwardIt last, BinaryPredicate p ); 

    The code may end up with something like this:

     std::sort( drv.begin(), drv.end(), sortbyPath ); auto it = std::unique( drv.begin(), drv.end(), []( const auto &a, const auto &b ) { return not sortbyPath( a, b ) and not sortByPath( b, a ); } ); drv.erase( it, drv.end() ); 

    Or you can write another function equalByPath() , which will create duplicate code, but will most likely be more efficient.

    If you sort only to leave unique ones, then it is not necessary to do this, it is simpler to use std::unordered_set and std::remove_if :

     std::unordered_set<std::wstring> uset; // тут была ошибка unoredered_set вместо unordered_set auto it = std::remove_if( drv.begin(), drv.end(), [&uset]( const auto &d ) { return not uset.insert( std::get<4>( d ) ).second; } ); drv.erase( it, drv.end() ); 
    • It is much easier to fill a set with the same data types from a vector and copy to a vector (if needed) - AR Hovsepyan
    • @ARHovsepyan is much easier than what? - Slava
    • how to execute std :: remove_if and drv.erase - AR Hovsepyan
    • Understood, the set meant std::set . There are nuances. The code will be easier, but the price will be high. First, copying the entire tuple, secondly, implementing std::unordered_set on this tuple will be somewhat nontrivial. Therefore, either the price will be even higher if you use std::set , or the code will not be much simpler, if not more difficult. - Slava
    • copying from a vector into a set is much easier and cheaper than dragging elements of a vector along a nontrivial predicate, followed by deletion. And the code is not needed at all, but you need to declare such a set <type> uset with one line (drv.begin (), drv.end ()) - AR Hovsepyan