2017-06-01 2 views
0

J'ai deux vecteurs de la forme std::vector<int> a et std::vector<double> b, par exempleconstatation et doublons effacement dans un vecteur et l'effacement des valeurs dans un autre vecteur

a= 1,2,3,3,4,5,6; 
b=0.1, 0.3, 0.2, 0.5, 0.6, 0.1, -0.2; 

les deux vecteurs sont de la même taille et en fait, ils fonctionnent comme une Paire XY ((1,0.1) , (2,0.3)...etc). Heureusement, a est triée de moins à plus toujours

Je veux trouver les doublons dans le premier vecteur, puis effacer le premier d'entre eux, dans mon exemple, la sortie doit être:

a= 1,2,3,4,5,6; 
b=0.1, 0.3, 0.5, 0.6, 0.1, -0.2; 

dans Matlab I ferait quelque chose comme ceci:

b(find(diff(a) == 0)) = []; 
a(find(diff(a) == 0)) = []; 

Je sais que je peux le faire à l'ancienne en utilisant des boucles et si des déclarations mais je suis sûr qu'il ya un moyen de faire plus élégant en C++ avec des conteneurs et itérateurs. En cherchant sur Internet, il y a beaucoup d'exemples pour effacer le doublon dans le premier vecteur mais pas pour utiliser les mêmes index pour effacer les éléments du deuxième vecteur.

Toute aide est appréciée.

+6

Ensuite, il devient trivial de faire ce que vous voulez. – NathanOliver

+5

Ou peupler vos données dans un 'std :: map ' initialement, et vous n'aurez plus besoin d'effacer les doublons puisque la carte ne supporte pas les clés dupliquées –

+0

Est-ce que vous stockez vraiment '0.3' dans votre vecteur' int'? – Galik

Répondre

0

Je ne pense pas qu'il existe un moyen de contourner l'utilisation des boucles et des instructions if.

iterator j = b.begin(); 
    iterator ahead = a.begin(); 
    ahead++; 
    while(1) { 
     if(ahead* == behind*) { // If we have a duplicate 
      a.erase(ahead);  // we need to erase the entry in a 
      b.erase(j);   // and the entry in b 
     } 
     else {     // Otherwise, just move on 
      j++; 
      ahead++; 
      behind++; 
     } 
     if(ahead == a.end()) // Once we reach the end of the vectors, end the loop 
      break; 
    } 

Cela pourrait fonctionner. Je ne sais pas exactement comment fonctionne erase(), mais je pense que la logique devrait fonctionner.

0

La raison pour laquelle vous trouverez quelques-uns (le cas échéant) des exemples de ce qui est bien écrit que la plupart des gens sont comme de commencer par définir quelque chose comme ceci:

struct coord { 
    int x; 
    double y; 

    // Since we want X values unique, that's what we compare by:  
    bool operator==(coord const &other) const { 
     return x == other.x; 
    } 
}; 

En utilisant, nous pouvons obtenir X unique et Y paires correspondantes sans boucles explicites assez facilement, car la bibliothèque standard fournit déjà des algorithmes à cette fin spécifique:

std::vector<coord> ab; 
// populate ab here ... 

// ensure only unique X values, removing the corresponding Y when we remove an X 
ab.erase(std::unique(ab.begin(), ab.end()), ab.end()); 

Si vous avez vraiment besoin de maintenir a et b sous forme de tableaux séparés à la place, je serais probablement encore faire quelque chose assez similaire, mais utilisez un zip iterator pour créer quelque chose qui ressemble/agit assez similaire que vous pouvez toujours utiliser unique et erase pour faire le travail.

0

Il doit y avoir un moyen plus facile que cela?

// compare the index vector by using the 
// values of another vector 
struct compare_by_other 
{ 
    std::vector<int>& v; 

    compare_by_other(std::vector<int>& v): v(v) {} 

    bool operator()(std::size_t idx1, std::size_t idx2) const 
     { return v[idx1] == v[idx2]; } 
}; 

std::vector<int> a = {1 , 2 , 3 , 3 , 3 , 4 , 4 , 5 }; 
std::vector<double> b = {0.2, 0.5, 0.1, 0.9, 2.5, 9.6, 0.3, 2.4}; 

// create an index to track which indexes need to be removed 
std::vector<std::size_t> indexes(a.size()); 
std::iota(std::begin(indexes), std::end(indexes), 0); 

// remove all the indexes that the corresponding vector finds duplicated 
auto end = std::unique(std::begin(indexes), std::end(indexes), compare_by_other(a)); 

// erase all those elements whose indexes do not appear in the unique 
// portions of the indexes vector 

a.erase(std::remove_if(std::begin(a), std::end(a), [&](auto& n){ 
    return std::find(std::begin(indexes), end, std::distance(a.data(), &n)) == end; 
}), std::end(a)); 

// same for b 

b.erase(std::remove_if(std::begin(b), std::end(b), [&](auto& n){ 
    return std::find(std::begin(indexes), end, std::distance(b.data(), &n)) == end; 
}), std::end(b)); 
0

Malheureusement, il n'y a pas de manière élégante que je connaisse pour le faire en C++.

Si vous êtes prêt à utiliser une bibliothèque, Range-V3 Eric Neibler (actuellement sur son chemin dans la norme) vous permet de faire cela d'une manière semi-agréable:

#include <range/v3/all.hpp> 
#include <iostream> 

namespace rng = ranges::v3; 

int main() 
{ 
    std::vector<int> a{1, 2, 3, 3, 4, 5, 6}; 
    std::vector<double> b{0.1, 0.3, 0.2, 0.5, 0.6, 0.1, -0.2}; 

    auto view = rng::view::zip(a, b); 

    auto result = rng::unique(view, [](auto&& x, auto&& y) { 
     return x.first == y.first; 
    }); 

    // This is a bit of a hack... 
    const auto new_end_idx = rng::distance(rng::begin(view), result); 

    a.erase(a.begin() + new_end_idx, a.end()); 
    b.erase(b.begin() + new_end_idx, b.end()); 

    std::cout << rng::view::all(a) << '\n'; 
    std::cout << rng::view::all(b) << '\n'; 
} 

Sortie:

[1,2,3,4,5,6] 
[0.1,0.3,0.2,0.6,0.1,-0.2] 

Wandbox link

il est pas encore tout à fait idéal (car il est impossible d'obtenir les premières itérateurs arrière d'un view::zip iterator comme autant que je peux dire), mais ce n'est pas trop mal.

0

Une suggestion sans code tout a parfaitement fonctionné:

facile, de manière moins efficace:

  1. Utilisez un zip iterator pour traiter les deux vecteurs comme une gamme unique de deux tuples/paires. (Il ne doit pas nécessairement s'agir de Boost, mais la bibliothèque standard n'a pas de AFAICR). Vous avez maintenant réduit le problème de filtrage dupes avec un critère de comparaison personnalisée (en supposant que vous ne me dérange pas la sortie est pas deux tableaux distincts)
  2. Construire un ensemble de deux triplets, en utilisant ce constructeur:

    template< class InputIt > 
    set(InputIt first, InputIt last, 
        const Compare& comp = Compare(), 
        const Allocator& alloc = Allocator()); 
    

    dans votre cas, l'allocateur par défaut est très bien, mais vous voulez régler le comparateur à quelque chose comme

    [](const std::tuple<int, double>& lhs, 
        const std::tuple<int, double>& rhs) -> bool 
    { 
         return std::get<0>(lhs) < std::get<0>(rhs); 
    } 
    

    ou vous pouvez écrire une fonction appropriée leur faisant même chose. Et cela dépend si votre itérateur zip expose des tuples ou des paires std :: pair bien sûr.

C'est tout!

Une chose plus efficace à faire serait de construire un vecteur de tuples, mais le peupler en utilisant un std::copy_if sur la plage de l'itérateur zippé. Au lieu d'avoir des vecteurs parallèles, pourquoi ne pas avoir un seul vecteur qui stocke les deux éléments de données dans un seul élément?