2013-04-14 2 views
0

J'ai trois System.Array où je stocke essentiellement des numéros de téléphone avec 10 caractères. La définition est la suivante:Rechercher si un élément est présent dans System.Array ou non et s'il existe supprimer de l'original

private static System.Array objRowAValues; 
private static System.Array objRowBValues; 
private static System.Array objRowCValues; 

Je le fais parce que je lis un énorme fichier Excel avec de nombreuses cellules (environ 1 000 000) et le processus avec List<string> est un peu lent. Plus tard dans mon code je change le System.Array à un List<string> principalement parce que je remplis un éléments ListBox. Ce sont comment puis-je faire la définition de la liste

private List<string> bd = new List<string>(); 
private List<string> bl = new List<string>(); 
private List<string> cm = new List<string>(); 

Je veux vérifier si toutes les valeurs de objRowAValues ​​existe dans objRowBValues ​​ou objRowCValues ​​et si EXISTE supprimer la valeur existante, cela? Je suis newbiew avec C# et c'est mes premiers pas.

EDIT: voici le code (parties pertinentes uniquement) de ce que je fais:

private List<string> bd = new List<string>(); 
private static System.Array objRowAValues; 

private List<string> bl = new List<string>(); 
private static System.Array objRowBValues; 

private List<string> cm = new List<string>(); 
private static System.Array objRowCValues; 

private List<string> pl = new List<string>(); 

private static Microsoft.Office.Interop.Excel.Application appExcel; 

Excel.Application xlApp; 
Excel.Workbook xlWorkBook; 
Excel.Worksheet xlWorkSheet; 
Excel.Range rngARowLast, rngBRowLast, rngCRowLast; 

long lastACell, lastBCell, lastCCell, fullRow; 

// this is the main method I use to load all three Excel files and fill System.Array and then convert to List<string> 

private void btnCargarExcel_Click(object sender, EventArgs e) 
    { 
     if (this.openFileDialog1.ShowDialog() == DialogResult.OK) 
     { 
      if (System.IO.File.Exists(openFileDialog1.FileName)) 
      { 
       Stopwatch stopWatch = new Stopwatch(); 
       stopWatch.Start(); 
       Thread.Sleep(10000); 

       filePath.Text = openFileDialog1.FileName.ToString(); 

       xlApp = new Microsoft.Office.Interop.Excel.Application(); 
       xlWorkBook = xlApp.Workbooks.Open(openFileDialog1.FileName, 0, true, 5, "", "", true, 
                Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, 
                false, 0, true, 1, 0); 
       xlWorkSheet = (Excel.Worksheet) xlWorkBook.Worksheets.get_Item(1); 

       fullRow = xlWorkSheet.Rows.Count; 
       lastACell = xlWorkSheet.Cells[fullRow, 1].End(Excel.XlDirection.xlUp).Row; 
       rngARowLast = xlWorkSheet.get_Range("A1", "A" + lastACell); 
       objRowAValues = (System.Array) rngARowLast.Cells.Value; 

       foreach (object elem in objRowAValues) { bd.Add(cleanString(elem.ToString(), 10)); } 

       nrosProcesados.Text = bd.Count().ToString(); 
       listBox1.DataSource = bd; 

       xlWorkBook.Close(true, null, null); 
       xlApp.Quit(); 

       releaseObject(xlWorkSheet); 
       releaseObject(xlWorkBook); 
       releaseObject(xlApp); 

       stopWatch.Stop(); 

       TimeSpan ts = stopWatch.Elapsed; 
       executiontime.Text = 
        String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, 
            ts.Milliseconds/10).ToString(); 
      } 
      else 
      { 
       MessageBox.Show("No se pudo abrir el fichero!"); 
       System.Runtime.InteropServices.Marshal.ReleaseComObject(appExcel); 
       appExcel = null; 
       System.Windows.Forms.Application.Exit(); 
      } 
     } 
    } 

    // This is the method where I clean the existent values from bd (the List where I should remove existent values on bl and existent values on cm 
    private void btnClean_Click(object sender, EventArgs e) 
    { 
     pl = bd; 
     Stopwatch stopWatch = new Stopwatch(); 
     stopWatch.Start(); 
     Thread.Sleep(10000); 

     pl.RemoveAll(element => bl.Contains((element))); 
     pl.RemoveAll(element => cm.Contains((element))); 

     textBox2.Text = pl.Count.ToString(); 
     listBox4.DataSource = pl; 

     stopWatch.Stop(); 

     TimeSpan ts = stopWatch.Elapsed; 
     textBox6.Text = 
      String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, 
          ts.Milliseconds/10).ToString(); 

    } 
+5

Si vous avez besoin de collections de tailles variables, n'utilisez pas de tableaux. Utilisez des listes génériques - ['List '] (http://msdn.microsoft.com/fr-fr/library/6sh2ey19.aspx), par exemple. Je suggère également de lire sur [LINQ] (http://msdn.microsoft.com/en-gb/library/vstudio/bb397926.aspx). – Oded

+0

@Oded J'ai édité le post, voir si c'est ce que vous référez sur votre commentaire et que puis-je faire avec LINQ? – Reynier

+0

Eh bien, c'est un début. Quant à LINQ - pourquoi ne pas lire l'introduction sur le lien que j'ai posté? – Oded

Répondre

1

Voici deux problèmes en fait:

  1. Comment trouver un moyen efficace de performance qu'une valeur existe dans un (énorme) collection ?
  2. Comment faire pour supprimer la valeur d'une collection qui est en cours d'énumération?

Pour une première Je recommande d'utiliser HashSet, comme Contains opération car il faut O (1) au lieu de O (n) pour List/Array.

Le second est un peu délicat - au lieu de parcourir la première collection, vous pouvez parcourir le reste et essayer de supprimer ces éléments de la première collection.

En cas de Hashset, Remove comprend la logique Contains et retourne bool pour indiquer si la valeur était dans la collecte, il est donc plus facile à utiliser pour la première collection HashSet aussi.

Il devrait ressembler à cela:

var a = new HashSet<string>(); 

    // OR, if you have existing array: 
    // var a = new HashSet<string>(myArray); 

    var b = new HashSet<string>(); 
    var c = new HashSet<string>(); 

    // ... some filling logic here ... 

    foreach (var item in b) 
    { 
     a.Remove(item); 
    } 

    foreach (var item in c) 
    { 
     a.Remove(item); 
    } 

après que vous pouvez utiliser ToList() pour lier des valeurs à votre contrôle - il créer un nouveau tableau avec toutes les valeurs non retirées:

List<string> aList = a.ToList(); 
+0

Je vais supprimer les valeurs existantes d'une liste non de b ou c comme vous le faites dans votre code, je dure à moins que je manque totalement, pourriez-vous s'il vous plaît poster un exemple en faisant cela?(en supprimant les valeurs de ** a **) vérifiez également mon précédent commentaire sur le remplissage de ListBox avec les valeurs – Reynier

+0

Mise à jour post pour répondre à vos questions. – Lanorkin

+0

Gentil homme, il a fallu seulement 00: 01: 23.39 minutes dans un PC avec moins de ressources (AMD A4 et 8 Go de RAM DDR3 au bureau) plus moins de 26 minutes dans l'autre PC, ça marche maintenant, merci beaucoup – Reynier

0

Sur la base des commentaires sur l'OP, voici ce que vous voulez faire:

...make your lists... 
bd.removeAll(phoneNum => bl.contains(phoneNum) || cm.contains(phoneNum)); 

Notez que ne doit pas utiliser arrays. Vous devrait utiliser plutôt List<string> pour les trois de ce qui précède.

+0

Je ne vous suis pas :-(supposons que je veux supprimer les valeurs de seulement ** bd ** donc en utilisant LINQ je peux faire quelque chose comme 'foreach (elem en bd) {bl.removeAll (i => i == elem);}' est-ce exact? – Reynier

+0

Je vous ai mal compris au départ, j'ai mis à jour en conséquence –

+0

J'ai également mis à jour avec de meilleurs noms de variables pour rendre la solution plus claire –

0

En supposant qu'ils sont Liste et que vous voulez éliminer uniquement les éléments bd, puisque vous n'avez pas dit clairement que les listes doivent être supprimés, je le ferais comme ceci:

bd.RemoveAll(element => bl.Contains((element))); 
bd.RemoveAll(element => cm.Contains((element))); 

Ou vous pouvez essayer d'utiliser HashSet

private List<string> hsA = new List<string>(); 
private List<string> hsB = new List<string>(); 
private List<string> hsC = new List<string>(); 

Et essayez d'utiliser la même approche:

hsA.RemoveWhere(element => hsB.Contains(element) || hsC.Contains(element)); 
+0

cette approche semble être bien mais c'est lent pour un PC avec un Core i3 550 et 12 Go de DDR3 donc quand fonctionne dans un PC avec moins de ressources, il va tout planter Voir cette image http://dropmocks.com/mBsHrd pour plus d'informations sur – Reynier

+0

Il faut 00: 26: 51,98 minutes pour traiter les opérations xD – Reynier

+0

J'ai édité ma réponse providi une approche différente qui pourrait être plus rapide. – Areks

0

Premièrement, si vous savez combien de lignes vous avez, essayez d'initialiser la liste avec une taille maximale appropriée. Donc, il ne doit pas redimensionner le tableau sous-jacent en permanence. Ou le type d'utilisation sauf tableaux string[] pour vous faire économiser quelques problèmes avec la coulée, etc.

Si vous souhaitez filtrer les RowAValues ​​liste, vous pouvez le faire avec Linq:

rowAValues = rowAValues.Where(x => !rowBValues.Contains(x) 
           && !rowCValues.Contains(x)) 
         .ToArray() 

Remplacer ToArray avec ToList si vous utilisez des listes bien sûr. Gardez à l'esprit que l'itération sur des milliers d'éléments nécessite beaucoup de performance. Si la vérification est exécutée plus d'une fois, utilisez un HashSet<string> pour les lignes b + c. Le code reste le même mais la recherche sera rapide.

Une bonne référence pour LINQ (langue de recherche intégré) est http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b

+0

Je ne sais pas combien de valeurs le tableau aura parce qu'un jour l'Excel pourrait avoir seulement 700 cellules mais un autre pourrait avoir 700 000 ou un autre pourrait avoir 7 000 000 donc pour cette raison je déclare la liste comme je le fais et comme pour utiliser 'HashSet' pour B et C en utilisant seulement en comparaison et rien d'autre pourrait être mais comment puis-je remplir un la ListBox avec des valeurs de ** rowBValues ​​** et ** rowCValues ​​**? ListBox.DataSource n'accepte que la liste un conseil à ce sujet? – Reynier

Questions connexes