2

Is there built in method to remove duplicates of pair vector if 2 pairs, {x,y} and {y,x} are also considered as duplicate?

for example,if I have vector like it:

{{1,2},{4,3},{2,1},{5,6},{1,2},{3,4},{0,1}}

I want to remove duplicate to become:

{{1,2},{4,3},{5,6},{0,1}}

is there any built in function to handle the case that {x,y} and {y,x} are identical?

If not, what is the simplest way to do this?

I considered to use for loop something like that but not works:

vector<int> isRemove;
int count=0;
for(pair<int,int> a : p){
    isRemove.push_back(0);
    for(pair<int,int> b : p){
        if((a.first==b.first && a.second==b.second) || (a.first==b.second && a.second==b.first)){
            isRemove[count]=1;
            break;
        }
    }
    count++;
}
for(int i=isRemove.size()-1;i>=0;i--){
    printf("%d\n",isRemove[i]);
    if(isRemove[i]){
        p.erase(p.begin()+i);
    }
}

is there any simpler method else?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
ggrr
  • 7,737
  • 5
  • 31
  • 53
  • 2
    http://stackoverflow.com/questions/7958216/c-remove-if-on-a-vector-of-objects - with a custom condition. – Luchian Grigore Sep 29 '15 at 10:12
  • 2
    The simple method would be to use `std::set` with a custom comparer so that you don't get duplicates in the first place. Is there a particular reason you need to use a vector? @LuchianGrigore That doesn't cover the comparing of different elements of the vector, does it? –  Sep 29 '15 at 10:36
  • boost library has some utilities for this. Very convenient to use as well. – Sravan Sep 29 '15 at 10:45

3 Answers3

2

std::set holds unique values. Uniqueness is determined by a comparator. You can implement your desired solution as follows (live example):

#include <algorithm>
#include <iostream>
#include <set>
#include <vector>

struct custom_comparator {
    bool operator()(const std::pair<int, int>& a,
                    const std::pair<int, int>& b) const
    {
        return less_comparator(std::minmax(a.first, a.second),
                               std::minmax(b.first, b.second));
    }

    std::less<std::pair<int, int>> less_comparator;
};

int main() {
    // Input data including some duplicates
    std::vector<std::pair<int, int>> a = {
        {1, 2}, {4, 3}, {2, 1}, {5, 6}, {5, 6}, {6, 5}, {1, 2}, {3, 4}, {0, 1}
    };

    // Specify custom comparator for the set
    std::set<std::pair<int, int>, custom_comparator> unique;

    // Fill the set
    for (const auto& p : a) {
        unique.insert(p);
    }

    // Demonstrate uniqueness by outputting the elements of the set
    for (const auto& p : unique) {
        std::cout << p.first << ", " << p.second << "\n";
    }

    return 0;
}

Output:

0, 1
1, 2
4, 3
5, 6

You simply define a set with a custom comparator that ensures a consistent ordering within each pair when calling std::less, then fill the set.

Andrew
  • 5,212
  • 1
  • 22
  • 40
  • 2
    Your comparison function should have a `const` call operator. – Jonathan Wakely Sep 29 '15 at 12:28
  • @JonathanWakely It certainly should. I'll fix that up. – Andrew Sep 29 '15 at 12:32
  • Your comparison function allows for a < b = c < a. You need to fix the comparison function to return consistent results, for instance by always placing the lower value first. –  Sep 29 '15 at 22:24
  • 1
    Huh, cool, I wouldn't have thought to use `std::minmax` for that, nice to pick up something new! :) –  Sep 29 '15 at 22:46
1
#include <vector>
#include <algorithm>
#include <iostream>
int main(){
  using namespace std;
  using Intpair = std::pair<int,int>;

  vector<Intpair> v = {{1,2},{4,3},{2,1},{5,6},{1,2},{3,4},{0,1}};
  //Normalize == sort the pair members
  for(auto& p : v){
    int x = max(p.first, p.second), y = min(p.first, p.second);
    p.first = x; p.second = y;
  }
  //Sort the pairs
  sort(v.begin(), v.end(),[](Intpair x, Intpair y){ return (x1 < y1) || (x1==y1 && x2<y2); }  );

  //Print the vector in its normalized and sorted form
  for(auto p : v){ cout<<p.first<<' '<<p.second<<'\n'; }
  cout<<'\n';
  //Unique the vector
  auto last = unique(v.begin(), v.end() );
  v.erase(last, v.end());

  //Print the unique'd vector
  for(auto p : v){ cout<<p.first<<' '<<p.second<<'\n'; }
}

Output:

1 0
2 1
2 1
2 1
4 3
4 3
6 5

1 0
2 1
4 3
6 5

This should have better performance for smaller vectors than what you'd get with std::set, as the latter isn't as cache friendly as vectors are.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
0

The following program will out put exactly you need:

#include <iostream>
#include <vector>


int main () {
  std::vector<std::pair<int, int>> myvector;
  myvector.push_back(std::make_pair(1,2));
  myvector.push_back(std::make_pair(4,3));
  myvector.push_back(std::make_pair(2,1));
  myvector.push_back(std::make_pair(5,6));
  myvector.push_back(std::make_pair(1,2));
  myvector.push_back(std::make_pair(3,4));
  myvector.push_back(std::make_pair(0,1));

  auto it = myvector.begin();
  for (; it != myvector.end(); ++it) {
      auto pit = myvector.begin();
      for (; pit != myvector.end();) {
          if (((it->first == pit->first && it->second == pit->second) || (it->first == pit->second && it->second == pit->first)) && (pit != it)) {
              std::cout << "found pair " << it->first << "," << it->second << " with " << pit->first << "," << pit->second << std::endl;
              pit = myvector.erase(pit);
          } else {
              ++pit;
          }
      }

  }

  std::cout << "myvector contains:";

  for (it=myvector.begin(); it!=myvector.end(); ++it)
    std::cout << ' ' << it->first << "," << it->second;

  std::cout << '\n';

  return 0;
}

Output:

myvector contains: 1,2 4,3 5,6 0,1
Denis Zaikin
  • 569
  • 4
  • 10