Avete un algoritmo per rimuovere i duplicati da una collection?

Rompo il ghiaccio con un nuovo argomento sul quale mi sono imbattuto ieri.

La mia necessità era “togliere i duplicati da una collection creata a runtime”.

La prima versione dell’algoritmo usava un nested foreach che però già con 6000 record (e un solo record duplicato: cioè su 6000 record un documento era stato aggiunto due volte alla collection) stava 9 secondi ad eseguirsi.

Poi ho cambiato approccio e sono riuscito a portare il tempo a 700ms.

Noto che le collection sono molto performanti, però per lo più ci si rirova a fare sempre foreach, il che dà problemi di performance quando le collection hanno migliaia di elementi.

Prima versione dell’algoritmo:

// questa fa un nested foreach quindi l'effort è esponenziale
foreach IDDocument d in myCollection
{
  string currentDocId = d.DocId()
  int counter = 0
  foreach IDDocument d1 in myCollection
  {
   string innerDocId = d1.DociI()
   if (innerDocId == currentDocId and counter < 1)
   {
     counter = counter + 1
     continue
   }
   if (innerDocId == currentDocId and counter >= 1)
   {
    d.deleted = true
    }
}

myCollection.removeDeleted()

Secionda versione dell’algoritmo:

// questa fa un loop interno solo se viene trovato un doppione, usa findDocuments
foreach IDDocument d in myCollection
{
  IDCollection duplicatedElements of IDDocument = myCollection.findDocuments(d)

   if (duplicatedElements.count() > 1)
   {
     int counter = 0
     foreach IDDocument d1 in duplicatedElements 
      {
        if (!(counter = 0))
        {
          d.deleted = true
         }
        counter= counter + 1
      }
   }
  
myCollection.removeDeleted()

Nel mio caso (in cui mi aspetto generalmente pochi duplicati) la seconda versione è molto più performante, ma in generale non so dire se è migliore in assoluto.

Voi avete sviluppato altri algoritmi?

Grazie. Ciao!

3 Mi Piace

Grazie del contributo @f.faleschini e benvenuto nella Community di Instant Developer.

Anch’io utilizzo il secondo metodo per cercare i duplicati.

Grazie del benvenuto @paolo.giannelli . Sono contento di aver “azzeccato” l’algoritmo. Le collection sono molto potenti però i foreach a volte sono lenti, però usando l’approccio di questo algoritmo si può in effetti evitare alcuni foreach, evidentemente è questa la strada per gestire collection enormi. Ciao!

Ciao Francesco
mi interessa molto il discorso ottimizzazione in generale.
Volevo chiederti una cosa rispetto alle due versioni che hai scrittto: la prima confronta il docid mentre la seconda confronta tutte le proprietà non nulle (almeno così è scritto nella documentazione del metodo findDocuments).
Quando hai fatto il benchmark i documenti avevano altre proprietà valorizzate rispetto al docid?
Appena posso voglio provare a buttare giù una mia versione ma dammi giusto questo chiarimento sul requisito “duplicato”: si intende duplicato se ha lo stesso docid o se tutte le proprietà del documento sono uguali?
Grazie
Stefano

Ciao Stefano,
ben ritrovato.

Hai ragione, la findDocuments è pesantissima, sarebbe comodo se avesse un parametro opzionale booleano “compareDocIdOnly” che la accelererebbe molto.

Io nel mio caso ho risolto il problema evitando i doppioni a priori: nel mio codice popolavo una collection “con leggerezza” e poi toglievo i duplicati, poi ho cambiato il codice che popolava la collection, così ero certo di non avere duplicati e questo ha migliorato di 2 ordini di grandezza la velocità della procedura.

Quindi per risponderti entrambi gli algoritmi sono inefficaci, forse migliorerebbe il secondo con il parametro che ho suggerito, ma siamo ancora nel campo delle ipotesi.

Ciao!

Ciao Francesco
per migliorare la seconda versione dell’algoritmo avevo pensato di creare un IdDocument temporaneo nel quale valorizzare solo le proprietà su cui si intende effettuare la ricerca.
Penso che farò un po’ di esperimenti, anche per capire se altre soluzioni totalmente diverse sono percorribili. Ti tengo aggiornato su eventuali scoperte.
Grazie!

Anche io faccio così, perché non è detto che si abbia DocID ovunque, ad esempio nelle tabelle “semplici” tipo le liste di regioni, province… ho solo il codice.

Finora non ho notato cali di prestazioni usando un IDDocument temporaneo, che distruggo appena dopo aver filtrato, l’unica “palla” è che appunto è necessario istanziare una classe, sarebbe molto comodo se in futuro si potrà passare degli oggetti al volo come si fa in JS su InDe Cloud :grinning:

Grazie per il contributo.

Una idea potrebbe anche essere invece di
FindDocuments(optional compareDocIdOnly: boolean)
avere
FindDocuments(optional propertyIndex: int)

quindi gli passo io quale properietà usare per il confronto.

Ciao!

1 Mi Piace