как отсортировать std :: vector по значениям другого std :: vector?

<p>у меня есть несколько <code>std::vector</code>, все одинаковой длины. я хочу отсортировать один из этих векторов и применить то же преобразование ко всем другим векторам. есть ли аккуратный способ сделать это? (желательно с использованием stl или boost)? некоторые из векторов содержат <code>int</code>s, а некоторые из них <code>std::string</code>s.</p><p>псевдокод:</p><pre><code>std::vector<int> index = { 3, 1, 2 }; std::vector<std::string> values = { "third", "first", "second" }; transformation = sort(index); index is now { 1, 2, 3}; ... magic happens as transformation is applied to values ... values are now { "first", "second", "third" }; </code></pre>

<p>подход фриола хорош в сочетании с твоим. сначала создайте вектор, состоящий из чисел 1…<em> n </em>, вместе с элементами вектора, определяющими порядок сортировки:</p><pre><code>typedef vector<int>::const\_iterator myiter; vector<pair<size\_t, myiter> > order(index.size()); size\_t n = 0; for (myiter it = index.begin(); it != index.end(); ++it, ++n) order[n] = make\_pair(n, it); </code></pre><p>теперь вы можете отсортировать этот массив, используя пользовательский сортировщик:</p><pre><code>struct ordering { bool operator ()(pair<size\_t, myiter> const& a, pair<size\_t, myiter> const& b) { return *(a.second) < *(b.second); } }; sort(order.begin(), order.end(), ordering()); </code></pre><p>теперь вы зафиксировали порядок перестановки внутри <code>order</code> (точнее, в первом компоненте элементов). теперь вы можете использовать этот порядок для сортировки других ваших векторов. вероятно, в то же время работает очень умный вариант на месте, но пока кто-то другой не придет с ним, вот один вариант, который не на месте. он использует <code>order</code> в качестве справочной таблицы для нового индекса каждого элемента.</p><pre><code>template <typename t> vector<t> sort\_from\_ref( vector<t> const& in, vector<pair<size\_t, myiter> > const& reference ) { vector<t> ret(in.size()); size\_t const size = in.size(); for (size\_t i = 0; i < size; ++i) ret[i] = in[reference[i].first]; return ret; } </code></pre>

<p>поместите ваши значения в <a href="http://www.boost.org/doc/libs/1\_36\_0/libs/multi\_index/doc/index.html">boost multi-index контейнер</a> , затем итерируйте, чтобы прочитать значения в нужном вам порядке. вы даже можете скопировать их в другой вектор, если хотите.</p>

<pre><code>typedef std::vector<int> int\_vec\_t; typedef std::vector<std::string> str\_vec\_t; typedef std::vector<size\_t> index\_vec\_t; class sequencegen { public: sequencegen (int start = 0) : current(start) { } int operator() () { return current++; } private: int current; }; class comp{ int\_vec\_t& \_v; public: comp(int\_vec\_t& v) : \_v(v) {} bool operator()(size\_t i, size\_t j){ return \_v[i] < \_v[j]; } }; index\_vec\_t indices(3); std::generate(indices.begin(), indices.end(), sequencegen(0)); //indices are {0, 1, 2} int\_vec\_t index = { 3, 1, 2 }; str\_vec\_t values = { "third", "first", "second" }; std::sort(indices.begin(), indices.end(), comp(index)); //now indices are {1,2,0} </code></pre><p>теперь вы можете использовать вектор "индексы" для индексации в вектор "значения".</p>

<p>мне приходит в голову только одно грубое решение: создать вектор, который является суммой всех других векторов (вектор структур, таких как {3, third, ...}, {1, first, ...}), а затем отсортировать вектор по первому полю, а затем снова разделить структуры . <br/><br/>вероятно, есть лучшее решение внутри boost или с использованием стандартной библиотеки.</p>

<p>вы можете определить собственный итератор «фасада», который будет делать то, что вам нужно. он будет хранить итераторы для всех ваших векторов или, альтернативно, извлекать итераторы для всех, кроме первого вектора, из смещения первого. сложность заключается в том, к чему этот итератор обращает внимание: придумайте что-то вроде boost :: tuple и умело используйте boost :: tie. (если вы хотите расширить эту идею, вы можете рекурсивно создавать эти типы итераторов, используя шаблоны, но вы, вероятно, никогда не захотите записывать тип этого - поэтому вам нужно либо c ++ 0x auto, либо функция-обертка для сортировки, которая принимает диапазоны)</p>

<p>я думаю, что вам<em> действительно </em>нужно (но поправьте меня, если я ошибаюсь), это способ получить доступ к элементам контейнера в некотором порядке.</p><p>вместо того, чтобы переставлять мою оригинальную коллекцию, я бы позаимствовал концепцию из базы данных: сохранить индекс, упорядоченный по определенному критерию. этот индекс является дополнительным косвенным указателем, который предлагает большую гибкость.</p><p>таким образом, можно генерировать несколько индексов в соответствии с различными членами класса.</p><pre><code>using namespace std; template< typename iterator, typename comparator > struct index { vector<iterator> v; index( iterator from, iterator end, comparator& c ){ v.reserve( std::distance(from,end) ); for( ; from != end; ++from ){ v.push\_back(from); // no deref! } sort( v.begin(), v.end(), c ); } }; template< typename iterator, typename comparator > index<iterator,comparator> index ( iterator from, iterator end, comparator& c ){ return index<iterator,comparator>(from,end,c); } struct mytype { string name; double number; }; template< typename iter > struct nameless : public binary\_function<iter, iter, bool> { bool operator()( const iter& t1, const iter& t2 ) const { return t1->name < t2->name; } }; template< typename iter > struct numless : public binary\_function<iter, iter, bool> { bool operator()( const iter& t1, const iter& t2 ) const { return t1->number < t2->number; } }; void indices() { mytype v[] = { { "me" , 0.0 } , { "you" , 1.0 } , { "them" , -1.0 } }; mytype* vend = v + \_countof(v); index<mytype*, nameless<mytype*> > byname( v, vend, nameless<mytype*>() ); index<mytype*, numless <mytype*> > bynum ( v, vend, numless <mytype*>() ); assert( byname.v[0] == v+0 ); assert( byname.v[1] == v+2 ); assert( byname.v[2] == v+1 ); assert( bynum.v[0] == v+2 ); assert( bynum.v[1] == v+0 ); assert( bynum.v[2] == v+1 ); } </code></pre>

<p>ответом ltjax является отличный подход, который на самом деле реализован в zip\_iterator boost <a href="http://www.boost.org/doc/libs/1\_43\_0/libs/iterator/doc/zip\_iterator.html">http://www.boost.org/doc/libs/1\_43\_0/libs/iterator/doc/zip\_iterator.html</a> </p><p>он упаковывает в tuple любые итераторы, которые вы предоставляете. </p><p>затем вы можете создать свою собственную функцию сравнения для сортировки на основе любой комбинации значений итератора в вашем кортеже. на этот вопрос это будет просто первый итератор в вашем кортеже.</p><p>приятной особенностью этого подхода является то, что он позволяет сохранять память каждого отдельного вектора смежной (если вы используете векторы и это то, что вы хотите). вам также не нужно хранить отдельный индексный вектор целых чисел.</p>

<p>немного более компактный вариант ответа xtofl, если вы просто хотите перебрать все свои векторы на основе одного вектора <code>keys</code>. создайте вектор перестановки и используйте его для индексации других ваших векторов.</p><pre><code>#include <boost/iterator/counting\_iterator.hpp> #include <vector> #include <algorithm> std::vector<double> keys = ... std::vector<double> values = ... std::vector<size\_t> indices(boost::counting\_iterator<size\_t>(0u), boost::counting\_iterator<size\_t>(keys.size())); std::sort(begin(indices), end(indices), [&](size\_t lhs, size\_t rhs) { return keys[lhs] < keys[rhs]; }); // now to iterate through the values array. for (size\_t i: indices) { std::cout << values[i] << std::endl; } </code></pre>

<p>это было бы добавлением к ответу конрада, поскольку это подход для варианта на месте применения порядка сортировки к вектору. во всяком случае, так как редактирование не пройдет, я выложу здесь</p><p>вот вариант на месте с немного более высокой временной сложностью, которая связана с примитивной операцией проверки логического значения. дополнительная сложность пространства связана с вектором, который может быть зависящей от пространства реализацией, зависящей от компилятора. сложность вектора может быть устранена, если сам данный порядок может быть изменен.</p><p>вот вариант на месте с немного более высокой временной сложностью, которая связана с примитивной операцией проверки логического значения. дополнительная сложность пространства связана с вектором, который может быть зависящей от пространства реализацией, зависящей от компилятора. сложность вектора может быть устранена, если сам данный порядок может быть изменен. это пример того, что делает алгоритм. если порядок 3 0 4 1 2, перемещение элементов, обозначенное индексами положения, будет 3 ---> 0; 0 ---> 1; 1 ---> 3; 2 ---> 4; 4 ---> 2.</p><pre><code>template<typename t> struct applyorderinplace { void operator()(const vector<size\_t>& order, vector<t>& vectoorder) { vector<bool> indicator(order.size(),0); size\_t start = 0, cur = 0, next = order[cur]; size\_t indx = 0; t tmp; while(indx < order.size()) { //find unprocessed index if(indicator[indx]) { ++indx; continue; } start = indx; cur = start; next = order[cur]; tmp = vectoorder[start]; while(next != start) { vectoorder[cur] = vectoorder[next]; indicator[cur] = true; cur = next; next = order[next]; } vectoorder[cur] = tmp; indicator[cur] = true; } } }; </code></pre>

<pre><code>**// c++ program to demonstrate sorting in vector // of pair according to 2nd element of pair #include <iostream> #include<string> #include<vector> #include <algorithm> using namespace std; // driver function to sort the vector elements // by second element of pairs bool sortbysec(const pair<char,char> &a, const pair<int,int> &b) { return (a.second < b.second); } int main() { // declaring vector of pairs vector< pair <char, int> > vect; // initialising 1st and 2nd element of pairs // with array values //int arr[] = {10, 20, 5, 40 }; //int arr1[] = {30, 60, 20, 50}; char arr[] = { ' a', 'b', 'c' }; int arr1[] = { 4, 7, 1 }; int n = sizeof(arr)/sizeof(arr[0]); // entering values in vector of pairs for (int i=0; i<n; i++) vect.push\_back( make\_pair(arr[i],arr1[i]) ); // printing the original vector(before sort()) cout << "the vector before sort operation is:\n" ; for (int i=0; i<n; i++) { // "first" and "second" are used to access // 1st and 2nd element of pair respectively cout << vect[i].first << " " << vect[i].second << endl; } // using sort() function to sort by 2nd element // of pair sort(vect.begin(), vect.end(), sortbysec); // printing the sorted vector(after using sort()) cout << "the vector after sort operation is:\n" ; for (int i=0; i<n; i++) { // "first" and "second" are used to access // 1st and 2nd element of pair respectively cout << vect[i].first << " " << vect[i].second << endl; } getchar(); return 0;`enter code here` }** </code></pre>

<p>с лямбдами c++ 11 и алгоритмами stl, основанными на ответах конрада рудольфа и габриэле д'антона:</p><pre><code>template< typename t, typename u > std::vector<t> sortvecabyvecb( std::vector<t> & a, std::vector<u> & b ){ // zip the two vectors (a,b) std::vector<std::pair<t,u>> zipped(a.size()); for( size\_t i = 0; i < a.size(); i++ ) zipped[i] = std::make\_pair( a[i], b[i] ); // sort according to b std::sort(zipped.begin(), zipped.end(), []( auto & lop, auto & rop ) { return lop.second < rop.second; }); // extract sorted a std::vector<t> sorted; std::transform(zipped.begin(), zipped.end(), std::back\_inserter(sorted), []( auto & pair ){ return pair.first; }); return sorted; } </code></pre>

<p>вот сравнительно простая реализация, использующая<em> отображение индекса </em>между упорядоченным и неупорядоченным <code>names</code>, который будет использоваться для сопоставления <code>ages</code> с упорядоченным <code>names</code>:</p><pre><code>void ordered\_pairs() { std::vector<std::string> names; std::vector<int> ages; // read input and populate the vectors populate(names, ages); // print input print(names, ages); // sort pairs std::vector<std::string> sortednames(names); std::sort(sortednames.begin(), sortednames.end()); std::vector<int> indexmap; for(unsigned int i = 0; i < sortednames.size(); ++i) { for (unsigned int j = 0; j < names.size(); ++j) { if (sortednames[i] == names[j]) { indexmap.push\_back(j); break; } } } // use the index mapping to match the ages to the names std::vector<int> sortedages; for(size\_t i = 0; i < indexmap.size(); ++i) { sortedages.push\_back(ages[indexmap[i]]); } std::cout << "ordered pairs:\n"; print(sortednames, sortedages); } </code></pre><p>для полноты картины ниже приведены функции <code>populate()</code> и <code>print()</code>:</p><pre><code>void populate(std::vector<std::string>& n, std::vector<int>& a) { std::string prompt("type name and age, separated by white space; 'q' to exit.\n>>"); std::string sentinel = "q"; while (true) { // read input std::cout << prompt; std::string input; getline(std::cin, input); // exit input loop if (input == sentinel) { break; } std::stringstream ss(input); // extract input std::string name; int age; if (ss >> name >> age) { n.push\_back(name); a.push\_back(age); } else { std::cout <<"wrong input format!\n"; } } } </code></pre><p>а также:</p><pre><code>void print(const std::vector<std::string>& n, const std::vector<int>& a) { if (n.size() != a.size()) { std::cerr <<"different number of names and ages!\n"; return; } for (unsigned int i = 0; i < n.size(); ++i) { std::cout <<'(' << n[i] << ", " << a[i] << ')' << "\n"; } } </code></pre><p>и, наконец, <code>main()</code> становится:</p><pre><code>#include <iostream> #include <sstream> #include <string> #include <vector> #include <algorithm> void ordered\_pairs(); void populate(std::vector<std::string>&, std::vector<int>&); void print(const std::vector<std::string>&, const std::vector<int>&); //======================================================================= int main() { std::cout << "\t\tsimple name - age sorting.\n"; ordered\_pairs(); } //======================================================================= // function definitions... </code></pre>