Effacer ou remplacer des doublons dans un CSV

Résolu
cryptic Messages postés 7 Date d'inscription mercredi 23 août 2023 Statut Membre Dernière intervention 24 août 2023 - Modifié le 23 août 2023 à 20:16
cryptic Messages postés 7 Date d'inscription mercredi 23 août 2023 Statut Membre Dernière intervention 24 août 2023 - 24 août 2023 à 14:21

Bonjour à tous. J'essais de résoudre mon problème de multiples façons depuis 2 jours mais je n'y parviens pas. J'aurais besoin de votre aide ou vos idées s.v.p.

J'ai un CSV #1 avec en-tête [PART,PRICE] qui contient 486578 lignes.

J'ai un autre CSV #2 avec en-tête [PART,PRICE] qui contient 29110 lignes.

Ces deux CSV sont des livres de prix.

Toutes les données du CSV #2 se retrouvent dans le CSV #1 mais la valeur (PRICE) est différente. (Il s'agit des pièces ayant une mise à jour du prix.) J'aimerais combiner les deux CSV mais remplacer les lignes du CSV #1 qui se retrouvent dans le CSV #2 par les valeurs (PRICE) du CSV #2.

J'ai essayé plusieurs façons.

Mais chaque fois il faut un temps énormes pour analyser/remplacer 486578 lignes 29110 fois.

Avez-vous une idée comment je pourrais procéder pour réduire le temps de mise à jour ?

Merci

8 réponses

Whismeril Messages postés 19064 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 juin 2024 657
23 août 2023 à 20:27

Ah p'tet pas

Mais chaque fois il faut un temps énormes pour analyser/remplacer 486578 lignes 29110 fois.

Si tous tes tests se sont basés sur 486578 * 29110 itérations alors ce n'est pas la peine de tout décrire, juste de confirmer ce point.


1
Whismeril Messages postés 19064 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 juin 2024 657
23 août 2023 à 21:31

Ha c'est mieux le code, merci 

J'ai aussi pensé que je pourrais peut-être effacer la donnée de la liste CSV #2 quand elle est trouvée. 

Le premier élément de la liste 1 chercher dans les 29110 de la liste 2.

Si trouvé, l'élément est effacé des 2 listes. Dans ce quand, plus on avance, moins il y a d'articles dans la liste 2.

486578 *29110, 486578 *29109, 486578 *29108, etc.

Non, tu vas chercher de nombreuses valeurs inutilement.

Il ne faut chercher que celles qui sont dans le fichier de mise à jour.

On va commencer par un truc bourrin, l'idée est que si les fichiers sont triés, quand tu as trouvé la première référence, la seconde sera plus loin dans le fichier donc pas besoin de repasser les lignes précédentes.

Je tâche de te faire un exemple dans la soirée


1
cryptic Messages postés 7 Date d'inscription mercredi 23 août 2023 Statut Membre Dernière intervention 24 août 2023 1
23 août 2023 à 22:05

J'ai trouvé !

L'opération se complète en 15 secondes.

Dim idsNotInB = dt2.AsEnumerable().[Select](Function(r) r.Field(Of String)("PART")).Except(dt1.AsEnumerable().[Select](Function(r) r.Field(Of String)("PART")))
        Dim dt3 As DataTable = (From row In dt2.AsEnumerable() Join id In idsNotInB On row.Field(Of String)("PART") Equals id Select row).CopyToDataTable()


        DataGridView3.DataSource = dt3
               dt3.Merge(dt1)

Merci beaucoup

1
Whismeril Messages postés 19064 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 juin 2024 657
23 août 2023 à 20:18

Bonjour

des idées oui, mais peut-être celles que tu as déjà testées. On gagnerait donc du temps si tu décrivais tes essais ou que tu postais les codes correspondant.

Pour poster un code, il faut faire comme décrit là https://codes-sources.commentcamarche.net/faq/11288-poster-un-extrait-de-code

Question subsidiaire est ce que tu arrives à charger toutes les lignes dans un tableau avec File.ReadAllLines ?


0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cryptic Messages postés 7 Date d'inscription mercredi 23 août 2023 Statut Membre Dernière intervention 24 août 2023 1
Modifié le 23 août 2023 à 21:19

1. J'ai ouvert chaque CSV dans un DataTable différent. J'arrive a charger toutes les données en quelques secondes (5-10) dans un DataGridView

'Création du DataTable 1 avec le CSV #2  
 Dim filepath1 As String = TextBox3.Text
        Dim sr1 As New StreamReader(filepath1)
        Dim f1 As File
        Dim fval1 As String()
        Dim dt1 As New DataTable
        Dim dr1 As DataRow

        sr1 = f1.OpenText(filepath1)

        fval1 = sr1.ReadLine().Split(",")

        For i As Integer = 0 To fval1.Length - 1
            dt1.Columns.Add(New DataColumn(fval1(i).ToString()))
        Next

        dr1 = dt1.NewRow

        While sr1.Peek() <> -1
            fval1 = sr1.ReadLine().Split(",")
            dr1 = dt1.NewRow
            For i As Integer = 0 To fval1.Length - 1
                dr1.Item(i) = fval1(i).ToString()
            Next

            dt1.Rows.Add(dr1)
        End While

        DataGridView1.DataSource = dt1
        MessageBox.Show("dt1 construit")


'Création du DataTable 2 avec le CSV #1
        Dim filepath2 As String = TextBox4.Text
        Dim sr2 As New StreamReader(filepath2)
        Dim f2 As File
        Dim fval2 As String()
        Dim dt2 As New DataTable
        Dim dr2 As DataRow

        sr2 = f2.OpenText(filepath2)

        fval2 = sr2.ReadLine().Split(",")

        For i As Integer = 0 To fval2.Length - 1
            dt2.Columns.Add(New DataColumn(fval2(i).ToString()))
        Next

        dr2 = dt2.NewRow

        While sr2.Peek() <> -1
            fval2 = sr2.ReadLine().Split(",")
            dr2 = dt2.NewRow
            For i As Integer = 0 To fval2.Length - 1
                dr2.Item(i) = fval2(i).ToString()
            Next

            dt2.Rows.Add(dr2)
        End While

        DataGridView2.DataSource = dt2
        MessageBox.Show("dt2 construit")

2. Ensuite je crée une liste des valeurs en double a supprimer.

'Faire une list des doublons et les supprimer de dt2

 Dim common As List(Of DataRow) = (From r1 In dt1.AsEnumerable()
                                          Join r2 In dt2.AsEnumerable() On r1("PART") Equals r2("PART")
                                          Select r1).ToList()
        Dim deletenumber As Integer = 0
        MessageBox.Show(common.Count & " doublons")
        For Each dr As DataRow In common
            For i As Integer = dt2.Rows.Count - 1 To 0 Step -1
                Dim drDelete As DataRow = dt2.Rows(i)
                If deletenumber = 1000 Then
                    MessageBox.Show(deletenumber.ToString & " Doublons effacés")
                End If
                If deletenumber = 5000 Then
                    MessageBox.Show(deletenumber.ToString & " Doublons effacés")
                End If
                If dr("PART").ToString() = dt2.Rows(i)("PART").ToString() Then
                    deletenumber = deletenumber + 1
                    drDelete.Delete()
                End If
            Next
            dt2.AcceptChanges()
        Next

        MessageBox.Show(deletenumber.ToString & " Doublons effacés")

        DataGridView3.DataSource = dt2

3. Je rajoute les données du dt1 dans le dt2. 

dt2.Merge(dt1, False)

Avec une petite quantité de données dans le CSV #2 ça fonctionne bien. Mais avec toutes les données c'est très long (J'ai arrêté après 15 minutes d'attente).

J'ai aussi essayé en chargeant les données de chaque CSV dans des DataGrid différents et enlever les lignes qui sont pareil.

C'est aussi long.

 For i = dgv.Rows.Count - 1 To 0 Step -1
           
            Dim srcRow As DataGridViewRow = dgv.Rows(i)
            'find matched rows in DataGridView2
            Dim rowsToRemove = DataGridView2.Rows.
        Cast(Of DataGridViewRow)().
        Where(Function(row) row.Cells("PART").Value = srcRow.Cells("PART").Value).
        ToList()
          
            Dim x As Integer = rowsToRemove.Count
            
            For Each dstRow In rowsToRemove
                DataGridView2.Rows.Remove(dstRow)
            Next
          
            If x > 0 Then dgv.Rows.Remove(srcRow)
        Next

J'ai commencé à vérifier pour créer un dictionnaire avec les données du CSV #2 mais je ne suis pas sûr si ça accélère le processus. Je n'ai jamais travaillé avec ce processus avant non plus.

0
Whismeril Messages postés 19064 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 juin 2024 657
23 août 2023 à 20:56

Pour la coloration, il faut choisir Basic.


OK.

Je ne vois pas trop l'intérêt de passer par une datable, d'autant que tu sais faire du linq qui marche pareil avec un tableau ou une liste.

Le Datagridview, oublie, c'est très lent, et pas pensé pour stocker les données, mais pour les afficher.

Est-ce que ton gros fichier est trié par "Part" ?

Si non, je suppose qu'il est utilisé par un autre logiciel. Est-ce que ça poserait un problème à ce logiciel qu'il le soit ?

Et du coup, est-ce que le petit fichier est trié ?

Si non, idem ?

Et pour ma question subsidiaire ?


0
cryptic Messages postés 7 Date d'inscription mercredi 23 août 2023 Statut Membre Dernière intervention 24 août 2023 1
23 août 2023 à 21:15

Oui les 2 fichiers sont triés par "PART" dans le même ordre de A-9

Pour la question subsidiaire je vais essayer.

J'ai aussi pensé que je pourrais peut-être effacer la donnée de la liste CSV #2 quand elle est trouvée. 

Le premier élément de la liste 1 chercher dans les 29110 de la liste 2.

Si trouvé, l'élément est effacé des 2 listes. Dans ce quand, plus on avance, moins il y a d'articles dans la liste 2.

486578 *29110, 486578 *29109, 486578 *29108, etc.

Ou peut-être une façon de ne plus chercher la ligne une fois trouvé puisqu'il est sûr que la valeur se retrouve qu'une seule fois dans la liste.

Donc si l'élément est trouvé à l'item 5 de la liste on sauve 29103 intéractions.

0
Whismeril Messages postés 19064 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 juin 2024 657
23 août 2023 à 22:15

Pour coder vite, j'ai choisi de créer un 3eme fichier mis à jour.

Si cette solution te convient, tu pourras remplacer ça par l'ajout d'une ligne dans le datagridview si tu veux.

Mon fichier 1 de test

A,12
B,13
CC,12
D,12
E,14
F,15
G,15
H,9

Mon fichier 2 de test

A,99
CC,99
F,99
G,99

Et le code de première intention.

    Private Sub MettreAJour()
        Dim sr1 As New StreamReader("Fichier 1.csv")
        Dim fval1 As String()
        Dim sr2 As New StreamReader("Fichier 2.csv")
        Dim fval2 As String()

        If File.Exists("Fichier mis à jour.csv") Then
            File.WriteAllText("Fichier mis à jour.csv", "") 'on vide le fichier s'il existe
        End If

        Dim sr3 As New StreamWriter("Fichier mis à jour.csv")

        fval2 = sr2.ReadLine().Split(",")
        While sr1.Peek() <> -1
            fval1 = sr1.ReadLine().Split(",")
            If fval1(0) = fval2(0) Then
                sr3.WriteLine(String.Join(","c, fval2)) 'on prend la ligne à mettre à jour
                If sr2.Peek() <> -1 Then
                    fval2 = sr2.ReadLine().Split(",")
                End If
            Else
                sr3.WriteLine(String.Join(","c, fval1))
            End If
        End While

        sr1.Close()
        sr2.Close()
        sr3.Close()

    End Sub

 Ça donne ça

A,99
B,13
CC,99
D,12
E,14
F,99
G,99
H,9

0
Whismeril Messages postés 19064 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 juin 2024 657
24 août 2023 à 08:30

Je m'aperçois ce matin que tu ad posté ta solution pendant que je rédigeais mon message.

Très bien, t'avais pas besoin de moi finalement ;) !


0
cryptic Messages postés 7 Date d'inscription mercredi 23 août 2023 Statut Membre Dernière intervention 24 août 2023 1
24 août 2023 à 14:21

Merci beaucoup pour ton temps. 

0
Rejoignez-nous