Bagaimana cara saya mengurutkan vektor pasangan berdasarkan elemen kedua dari pasangan?

133

Jika saya memiliki vektor pasangan:

std::vector<std::pair<int, int> > vec;

Apakah ada dan cara mudah untuk mengurutkan daftar dalam urutan yang meningkat berdasarkan elemen kedua dari pasangan?

Saya tahu saya bisa menulis objek fungsi kecil yang akan melakukan pekerjaan, tetapi apakah ada cara untuk menggunakan bagian-bagian STL yang ada dan std::lessmelakukan pekerjaan secara langsung?

EDIT: Saya mengerti bahwa saya bisa menulis fungsi atau kelas terpisah untuk diteruskan ke argumen ketiga untuk mengurutkan. Pertanyaannya adalah apakah saya bisa membuatnya dari barang standar. Saya benar-benar sesuatu yang terlihat seperti:

std::sort(vec.begin(), vec.end(), std::something_magic<int, int, std::less>());
David Norman
sumber
Berikut ini contohnya: <br> std :: sortir dalam vektor berpasangan
LeppyR64
1
c ++ tidak memiliki lamdas sehingga Anda tidak dapat melakukan apa yang Anda inginkan, Anda harus membuat fungsi / functor terpisah. Ini bisa menjadi satu baris sehingga benar-benar tidak boleh menjadi masalah besar.
Evan Teran
1
C ++ memiliki lambdas sekarang! Merayu!
David Poole

Jawaban:

212

EDIT : menggunakan c ++ 14, solusi terbaik adalah sangat mudah untuk menulis berkat lambdas yang sekarang dapat memiliki parameter tipe auto. Ini adalah solusi favorit saya saat ini

std::sort(v.begin(), v.end(), [](auto &left, auto &right) {
    return left.second < right.second;
});

Cukup gunakan komparator khusus (ini merupakan argumen ke-3 opsional untuk std::sort)

struct sort_pred {
    bool operator()(const std::pair<int,int> &left, const std::pair<int,int> &right) {
        return left.second < right.second;
    }
};

std::sort(v.begin(), v.end(), sort_pred());

Jika Anda menggunakan kompiler C ++ 11, Anda dapat menulis yang sama menggunakan lambdas:

std::sort(v.begin(), v.end(), [](const std::pair<int,int> &left, const std::pair<int,int> &right) {
    return left.second < right.second;
});

EDIT : sebagai tanggapan atas suntingan Anda terhadap pertanyaan Anda, inilah beberapa pemikiran ... jika Anda benar-benar ingin menjadi kreatif dan dapat menggunakan kembali konsep ini banyak, cukup buat templat:

template <class T1, class T2, class Pred = std::less<T2> >
struct sort_pair_second {
    bool operator()(const std::pair<T1,T2>&left, const std::pair<T1,T2>&right) {
        Pred p;
        return p(left.second, right.second);
    }
};

maka Anda dapat melakukan ini juga:

std::sort(v.begin(), v.end(), sort_pair_second<int, int>());

atau bahkan

std::sort(v.begin(), v.end(), sort_pair_second<int, int, std::greater<int> >());

Meskipun jujur, ini semua sedikit berlebihan, tulis saja fungsi 3 baris dan lakukan dengan itu :-P

Evan Teran
sumber
Ingatlah bahwa ini berbeda dari operator<pada pair<T1,T2>. Komparator default menggunakan kedua elemen pertama dan kedua (dalam kasus yang pertama adalah sama). Di sini hanya yang kedua yang digunakan.
Googol
@Googol: Persis seperti yang diminta OP ... Dia berkata: "is there and easy way to sort the list in increasing order based on the second element of the pair?"
Evan Teran
@ evan-teran, ya, saya tahu. Saya hanya menunjukkan bahwa jika kedua elemen detik sama, maka hasilnya dapat membingungkan (jika digunakan untuk menyortir, misalnya). Masalah ini tidak diderita oleh pembanding default karena menggunakan elemen kedua untuk tie-breaking. Saya mencapai pertanyaan ini mencari pembanding yang menggunakan elemen kedua sebagai informasi utama untuk membandingkan, tetapi saya juga membutuhkan itu menggunakan yang pertama untuk tie-breaking, jadi saya ingin menghindari orang lain melewatkan titik itu (seperti saya, dalam fakta, memang).
Googol
71

Anda dapat menggunakan boost seperti ini:

std::sort(a.begin(), a.end(), 
          boost::bind(&std::pair<int, int>::second, _1) <
          boost::bind(&std::pair<int, int>::second, _2));

Saya tidak tahu cara standar untuk melakukan ini sama pendek dan ringkasnya, tetapi Anda bisa ambil boost::binditu semua terdiri dari header.

Johannes Schaub - litb
sumber
1
+1 untuk menggunakan Peningkatan. Btw, dengan kompiler modern Anda mungkin sudah bisa mengganti boost dengan std :: tr1 karena ini akan segera menjadi standar.
Andreas Magnusson
sayangnya, saya mencoba hal yang sama dengan gcc trunk c ++ 1x std :: bind, dan gagal karena tidak memiliki op <untuk bind. Entah apakah apa yang dikatakan c ++ 1x tentang ini. mungkin itu memberitahu Anda untuk menggunakan lambda untuk itu :)
Johannes Schaub - litb
1
Saya kira dorongan bukan standar, tapi cukup dekat. :-)
David Norman
Diposting pertanyaan tindak lanjut untuk jawaban ini di sini: stackoverflow.com/q/4184917/220636
nabulke
34

Cukup sederhana, Anda menggunakan fungsi sortir dari algoritma dan menambahkan fungsi bandingkan sendiri

vector< pair<int,int > > v;
sort(v.begin(),v.end(),myComparison);

Sekarang Anda harus membuat perbandingan berdasarkan pilihan kedua jadi nyatakan Anda "myComparison" sebagai

bool myComparison(const pair<int,int> &a,const pair<int,int> &b)
{
       return a.second<b.second;
}
Ezio
sumber
5
Sederhana dan "to-the-point". Tidak perlu peningkatan atau versi C ++ tertentu. +1
Thomio
1
Ini harus ditandai sebagai solusi terbaik. Tidak perlu c ++ 14 untuk mengimplementasikannya.
Kartik Chauhan
Bisakah Anda jelaskan kepada saya bagaimana perbandingan ini bekerja? Apakah kita melewatkan dua elemen ke perbandingan saya sekaligus, lalu bagaimana cara memilah? Juga, Peran apa yang dimainkan a.second <b.second?
era s'q
30

Dengan C ++ 0x kita dapat menggunakan fungsi lambda:

using namespace std;
vector<pair<int, int>> v;
        .
        .
sort(v.begin(), v.end(),
     [](const pair<int, int>& lhs, const pair<int, int>& rhs) {
             return lhs.second < rhs.second; } );

Dalam contoh ini jenis pengembalian booldideduksi secara implisit.

Lambda mengembalikan tipe

Ketika fungsi lambda memiliki pernyataan tunggal, dan ini adalah pernyataan kembali, kompilator dapat menyimpulkan tipe kembali. Dari C ++ 11, §5.1.2 / 4:

...

  • Jika pernyataan majemuk adalah dalam bentuk { return expression ; }, jenis ekspresi yang dikembalikan setelah konversi nilai-ke-nilai (4.1), konversi array-ke-pointer (4.2), dan konversi fungsi-ke-pointer (4.3);
  • jika tidak void,.

Untuk secara eksplisit menentukan jenis pengembalian gunakan formulir []() -> Type { }, seperti di:

sort(v.begin(), v.end(),
     [](const pair<int, int>& lhs, const pair<int, int>& rhs) -> bool {
             if (lhs.second == 0)
                 return true;
             return lhs.second < rhs.second; } );
Andreas Spindler
sumber
1
Mengapa if (lhs.second == 0)?
the swine
Tidak ada makna khusus; lhs.second < rhs.seconddapat kembali trueatau falsedan kompilator dapat dengan jelas menyimpulkan bool. Hanya ingin menunjukkan []() -> Type { }kasusnya.
Andreas Spindler
Setidaknya dengan dentang, deduksi implisit ini mungkin tidak berfungsi dengan baik, saya harus menambahkan -> bool sebagai tipe pengembalian lambda untuk membuatnya bekerja dengan benar.
MoDJ
5

Untuk sesuatu yang dapat digunakan kembali:

template<template <typename> class P = std::less >
struct compare_pair_second {
    template<class T1, class T2> bool operator()(const std::pair<T1, T2>& left, const std::pair<T1, T2>& right) {
        return P<T2>()(left.second, right.second);
    }
};

Anda dapat menggunakannya sebagai

std::sort(foo.begin(), foo.end(), compare_pair_second<>());

atau

std::sort(foo.begin(), foo.end(), compare_pair_second<std::less>());
Leon Timmermans
sumber
1

Anda harus bergantung pada select2nd yang tidak standar

Greg Rogers
sumber
-1

Cobalah menukar elemen pasangan sehingga Anda dapat menggunakannya std::sort()seperti biasa.

hadizadeh.ali
sumber