2010-03-08 14 views
3

Bon d'abord j'ai pensé que ce serait assez simple. Mais je ne peux pas penser à un moyen efficace de résoudre cela. Je pensais que c'était une façon brute de résoudre ça, mais ce n'est pas très élégant. J'ai une ArrayList. Contacts est une classe VO qui a plusieurs membres - nom, régions, id. ArrayList contient des doublons car différentes régions apparaissent plusieurs fois. La liste est triée par ID. Voici un exemple:Supprimer les doublons d'une ArrayList triée tout en conservant certains éléments des doublons

Entrée 0 - Nom: John Smith; Région: N; ID: 1
Entrée 1 - Nom: John Smith; Région: MW; ID: 1
Entrée 2 - Nom: John Smith; Les régions; ID: 1
Entrée 3 - Nom: Jane Doe; Région: NULL ID: 2
Entrée 4 - Nom: Jack Black; Région: N; ID: 3
Entrée 6 - Nom: Jack Black; Région: MW; ID: 3
Entrée 7 - Nom: Joe Don; Région: NE; ID: 4

Je veux transformer la liste ci-dessous en combinant des régions dupliquées ensemble pour le même ID. Par conséquent, la liste finale devrait avoir seulement 4 éléments distincts avec les régions combinées.

Ainsi, la sortie devrait ressembler à ceci: -

Entrée 0 - Nom: John Smith; Région: N, MW, S; ID: 1
Entrée 1 - Nom: Jane Doe; Région: NULL ID: 2
Entrée 2 - Nom: Jack Black; Région: N, MW; ID: 3
Entrée 3 - Nom: Joe Don; Région: NE; ID: 4

Que pensez-vous de la manière optimale de résoudre ce problème? Je ne cherche pas de code réel, mais des idées ou des conseils pour trouver le meilleur moyen de le faire.

Merci pour votre temps !!!

Répondre

2

Vous pouvez les itérer en les vidant (et en fusionnant des doublons) dans une TreeMap. Créez ensuite une liste à partir de la vue triée des valeurs de TreeMap.

Dans l'exemple de code, je suppose que vous avez une classe Entry avec les champs id, name et regions, ce dernier étant une liste d'instances de la région. Cela pourrait facilement être changé en Set, et en Région en Strings ou tout ce que vous utilisez. L'exemple copie les entrées avant de les insérer dans la carte, car elles seront modifiées lors de la fusion avec d'autres entrées.

SortedMap<Integer, Entry> mergedEntriesMap = new TreeMap<Integer, Entry>(); 
for (Entry e : entries) { 
    if (mergedEntriesMap.contains(e.id)) { 
    Entry m = mergedEntriesMap.get(e); 
    m.regions.addAll(e.regions); 
    } else { 
    Entry m = new Entry(); 
    // copy the entry to keep the original array clean 
    m.id = e.id; 
    m.name = e.name; 
    m.regions = new ArrayList<Region>(e.regions); 
    mergedEntriesMap.put(m.id, m); 
    } 
} 

List<Entry> mergedEntries = new ArrayList<Entry>(mergedEntriesMap.values()); 
+1

'TreeMap' répond' containsKey' dans 'O (log N)'. Cette solution est 'O (N log N)' et n'est donc pas optimale. – polygenelubricants

+0

optimal est un concept assez nébuleux. L'OP pourrait simplement utiliser un HashMap, mais s'il s'agit d'un très gros ensemble de données, le code ci-dessus est une très bonne solution. Une optimisation consisterait à ne pas utiliser l'appel de contains() - appelez simplement get() et construisez new si get() renvoie null. L'utilisation de SortedMap ici n'aide pas vraiment, cependant - n'importe quelle implémentation de carte fonctionnerait. –

+0

Il voulait que la sortie soit triée, si l'entrée est également triée, vous pouvez la résoudre en O (N) en l'itérant et en n'attendant que des fusions dans des entrées consécutives. Je me suis dit qu'il traitait déjà un O (N log N) lors du tri préliminaire de la liste d'entrée ou du tri de la liste de sortie afin que ma solution essaie de résoudre à la fois la fusion et le tri en une fois. –

1

Ceci est un pseudocode pour accomplir ce que vous voulez. Au niveau abstrait, vous avez une liste de Pair<K,V> (first, second), triées par K, et pas deux paires sont vraiment égaux (vous pouvez avoir (k1,v1) et (k1,v2), mais vous ne pouvez pas avoir deux (k1,v1) dans la liste.

Vous voulez fusionner des paires consécutives (k,v1),(k,v2),(k,v3) à un groupe (k,[v1,v2,v3]).

List<Pair<K,V>> in; 
List<Pair<K,List<V>>> out = [ ]; 

Pair<K,V> lastP = SENTINEL_PAIR; // lastP.first matches nothing 
Pair<K,List<V>> lastGroup; 

for (Pair<K,V> p : in) { 
    if (p.first == lastP.first) { // same group as last 
    lastGroup.second.add(p.second); 
    } else {      // start a new group 
    lastGroup = (p.first, [ p.second ]); 
    out.add(lastGroup); 
    } 
    lastP = p; 
} 

Dans votre cas, K est l'ID et V est la région. Ceci est O(N).

+0

Vous pouvez utiliser un multimap de jakarta commons pour le faire plus élégamment. – Rahul

+0

Merci pour votre réponse. Soigné. – CoolBeans

2

Les données initiales sont-elles bloquées dans ce format? Si ce n'est pas le cas, vous souhaiterez peut-être modifier la requête que vous utilisez pour récupérer vos données en regroupant tous les identifiants et en formant une colonne de liste séparée par des virgules.Voici un exemple dans sql

SELECT  Id, [Name], Regions = replace 
      ((SELECT Region AS [data()] 
      FROM RegionTable 
      WHERE Id = u.Id 
      ORDER BY Region FOR xml path('')), ' ', ', ') 
FROM  [User] u 
WHERE  Id IS NOT NULL 
GROUP BY Id, [Name] 
+0

Aha, je ne savais pas que vous pourriez combiner plusieurs lignes de données dans une seule ligne de cette façon en utilisant sql. Non, les données ne sont pas bloquées dans ce format. Je peux modifier le sql. Cela va contre DB2. Je suis familier avec la fonction REPLACE, cependant, je ne suis pas sûr de pouvoir faire FOR après ORDER BY de cette façon dans DB2 ou non. Les données ne sont pas au format XML, mais uniquement des données en texte brut. Merci! – CoolBeans

0

Avez-vous pris un coup d'oeil chez Multimap google? Il est à peu près créé pour ce type de structure de données dans lequel il y a une clé qui correspond à Collection d'éléments. Dans ce cas, un nom String est mappé à un objet Collection de Region.

Multimap<String, Region> names = HashMultimap.create(); 
for (Entry entry : entries) { 
    names.put(entry.getName(), entry.getRegion()); 
} 
// Now u can get the collection of regions by name 
Collection<Region> johnsRegions = names.get("John Smith"); 
+0

On dirait que Jakarta en offre un similaire. Merci pour le conseil. – CoolBeans