IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Remplacement de valeur dans DataTable


Sujet :

C#

  1. #1
    Membre émérite Avatar de kheironn
    Homme Profil pro
    Chef de projets technique C# / MVC / .Net
    Inscrit en
    Février 2007
    Messages
    823
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projets technique C# / MVC / .Net
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2007
    Messages : 823
    Par défaut Remplacement de valeur dans DataTable
    Bonjour.
    Je suis un peu rouillé sur les DataTable et confronté à deux problèmes.
    soit le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    private static DataSet MergeTableLignesByField(DataSet dataSet, DataTable originalTable, int columnIndex)
            {
                if(originalTable.Rows.Count > 0 && originalTable.Rows[0].ItemArray[columnIndex] is string)
                {
                    DataTable newTable = originalTable.Clone();
                    newTable.Clear();
     
                    DataRow previousRow = null;
                    for(int i = 0; i < originalTable.Rows.Count; i++)
                    {
                        DataRow currentRow = originalTable.Rows[i];
                        bool matchLine = false;
                        if(previousRow != null)
                        {
                            // On compare les champs de deux lignes pour savoir si on peut les fusionner : tous les champs sont identiques sauf le champ spécifié
                            for(int j = 0; j < currentRow.ItemArray.Length; j++)
                                if(j != columnIndex)
                                {
                                    matchLine = (currentRow.ItemArray[j].ToString() == previousRow.ItemArray[j].ToString());
                                    if(!matchLine)
                                        break;
                                }
                            //Si les lignes sont identiques, on peut les fusionner et merger le champ voulu.
                            if(matchLine)
                            {
                                previousRow.BeginEdit();
                                string newValue = string.Concat(previousRow.ItemArray[columnIndex].ToString(), " | ", currentRow.ItemArray[columnIndex].ToString());
                                previousRow.ItemArray[columnIndex] = newValue;
                                previousRow.AcceptChanges();
                                previousRow.EndEdit();
                            }
                        }
     
                        if(!matchLine)
                        {
                            if(previousRow != null)
                                newTable.Rows.Add(previousRow.ItemArray);
                            previousRow = newTable.NewRow();
                            previousRow.ItemArray = currentRow.ItemArray;
                        }
     
                    }
                    //originalTable.Clear();
                    //originalTable.Merge(newTable, true);
                }
                return dataSet;
            }
    qui est censé faire :
    Dans une table d'un DataSet, parcourir toutes les lignes successives, si on trouve plusieurs lignes avec les mêmes valeurs (sauf pour le champ indiqué par columnIndex) on ne prend qu'une ligne et on concatène le contenu du champ [columnIndex] de toutes les lignes qui matches.
    C'est clair ?

    Premier problème : previousRow.ItemArray[columnIndex] = newValue; ne change pas de valeur.
    2ème pbm : c'est une usine à gaz... Il n'y aurait pas plus simple ?
    Merci


    p.s. :
    mes données sont du genre :
    nom1,prénom1,champ à concat 1, integer
    nom1,prénom1,champ à concat 2, integer
    nom2,prénom2,champ à concat 1, integer
    nom2,prénom2,champ à concat 2, integer
    nom3,prénom3,"texte", integer
    et je veux fusionner les lignes 1 & 2 , et 3 & 4

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Tu peux faire ça avec Linq, il faut juste créer un IEqualityComparer spécifique pour comparer les lignes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    void MergeTableLignesByField(DataTable table, int columnIndex)
    {
        var comparer = new DataRowComparer(columnIndex);
        var groups = table.AsEnumerable().GroupBy(r => r, comparer);
        foreach (var g in groups)
        {
            MergeRows(g, columnIndex);
        }
    }
     
     
    void MergeRows(IEnumerable<DataRow> rows, int columnToMerge)
    {
        string mergedValue = string.Join("|", rows.Select(r => r[columnToMerge]));
        int count = 0;
        foreach (var row in rows)
        {
            if (count == 0)
                row[columnToMerge] = mergedValue;
            else
                row.Delete();
            count++;
        }
    }
     
    class DataRowComparer : IEqualityComparer<DataRow>
    {
        private readonly int[] _excludedColumnIndexes;
        public DataRowComparer(params int[] excludedColumnIndexes)
        {
            _excludedColumnIndexes = excludedColumnIndexes;
        }
     
        public bool Equals(DataRow x, DataRow y)
        {
            for (int i = 0; i < x.Table.Columns.Count; i++)
            {
                if (_excludedColumnIndexes.Contains(i))
                    continue;
                if (!Equals(x[i], y[i]))
                    return false;
            }
            return true;
        }
     
        public int GetHashCode(DataRow row)
        {
            unchecked // Overflow is fine, just wrap
            {
                int hash = 17;
                for (int i = 0; i < row.Table.Columns.Count; i++)
                {
                    if (_excludedColumnIndexes.Contains(i))
                        continue;
                    object fieldValue = row[i];
                    int fieldHash = fieldValue != null ? fieldValue.GetHashCode() : 0;
                    hash = hash * 23 + fieldHash;
                }
                return hash;
            }
        }
    }
    (il faut référencer l'assembly System.Data.DataSetExtensions)

    Au passage, j'ai fait une autre modif : le paramètre dataSet de ta méthode n'est pas utilisé, il est juste renvoyé tel quel, donc je l'ai viré et j'ai mis le type de retour en void

  3. #3
    Membre émérite Avatar de kheironn
    Homme Profil pro
    Chef de projets technique C# / MVC / .Net
    Inscrit en
    Février 2007
    Messages
    823
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projets technique C# / MVC / .Net
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2007
    Messages : 823
    Par défaut
    J'ai modifié mon code, la substitution s'opère :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    private static DataSet MergeTableLignesByField(DataSet dataSet, DataTable originalTable, int columnIndex)
            {
                if(originalTable.Rows.Count > 0 && originalTable.Rows[0].ItemArray[columnIndex] is string)
                {
                    DataTable newTable = originalTable.Clone();
                    newTable.Clear();
     
                    object[] newValues = null;
                    for(int i = 0; i < originalTable.Rows.Count; i++)
                    {
                        DataRow currentRow = originalTable.Rows[i];
                        bool matchLine = false;
                        if(newValues != null)
                        {
                            // On compare les champs de deux lignes pour savoir si on peut les fusionner : tous les champs sont identiques sauf le champ spécifié
                            for(int j = 0; j < currentRow.ItemArray.Length; j++)
                                if(j != columnIndex)
                                {
                                    matchLine = (currentRow.ItemArray[j].ToString() == newValues[j].ToString());
                                    if(!matchLine)
                                        break;
                                }
                            //Si les lignes sont identiques, on peut les fusionner et merger le champ voulu.
                            if(matchLine)
                            {
                                newValues[columnIndex] = string.Concat(newValues[columnIndex].ToString(), " | ", currentRow.ItemArray[columnIndex].ToString());
                               // previousRow.ItemArray = newValues;
                            }
                        }
     
                        if(!matchLine)
                        {
                            if(newValues != null)
                                newTable.Rows.Add(newValues);
                            newValues = currentRow.ItemArray;
                        }
     
                    }
                    originalTable.Clear();
                    originalTable.Merge(newTable, true);
                }
                return dataSet;
            }

    J'ai vu ton code, Tomlev, je testerai pour ma culture => le besoin n'existe plus, on s'est aperçu qu'il existe des cas pour lesquels le problème de ligne ne serait pas réglé comme ça.

    Mon DataSet, c'est parce que c'est du code appelé par 4 surcharges d'une méthode d'extension. Je pensais que c'était nécessaire :
    voici l'un des surcharges :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
            public static DataSet MergeTableLignesByField(this DataSet dataSet, string tableName, string fieldName)
            {
                DataTable originalTable = dataSet.Tables[tableName];
                int columnIndex = originalTable.Columns[fieldName].Ordinal;
                return MergeTableLignesByField(dataSet, originalTable, columnIndex);
            }
    Merci à toi.

  4. #4
    Membre émérite Avatar de kheironn
    Homme Profil pro
    Chef de projets technique C# / MVC / .Net
    Inscrit en
    Février 2007
    Messages
    823
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projets technique C# / MVC / .Net
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2007
    Messages : 823
    Par défaut
    Tomlev
    Pour ma culture, j'ai testé ton code.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    rows.Select(r => r[columnToMerge])
    me retourne null. Je ne comprends pas pourquoi.

  5. #5
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par kheironn Voir le message
    Tomlev
    Pour ma culture, j'ai testé ton code.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    rows.Select(r => r[columnToMerge])
    me retourne null. Je ne comprends pas pourquoi.
    Bizarre, pourtant j'ai testé et chez moi ça fonctionne bien...

    Qu'est-ce qui retourne null d'ailleurs ? Le Select ? normalement c'est pas possible, ça devrait renvoyer un IEnumerable<object>... Ou alors c'est juste r[columnToMerge] qui renvoie null pour certains r ? normalement c'est pas possible non plus, ça devrait renvoyer DBNull s'il n'y a pas de valeur

  6. #6
    Membre émérite Avatar de kheironn
    Homme Profil pro
    Chef de projets technique C# / MVC / .Net
    Inscrit en
    Février 2007
    Messages
    823
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projets technique C# / MVC / .Net
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2007
    Messages : 823
    Par défaut
    rows contient mes trois lignes (normal) et ajoute une dernière ligne null.
    Ce serait donc la méthode de création des groupes qui ne va pas.

  7. #7
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par kheironn Voir le message
    rows contient mes trois lignes (normal) et ajoute une dernière ligne null.
    Bizarre... pourtant les lignes de rows viennent d'un GroupBy sur les lignes de la table, et la table ne peut pas contenir une ligne nulle...

    Sinon tu peux toujours essayer d'ajouter un .Where(r => r != null) avant le Select

  8. #8
    Membre émérite Avatar de kheironn
    Homme Profil pro
    Chef de projets technique C# / MVC / .Net
    Inscrit en
    Février 2007
    Messages
    823
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projets technique C# / MVC / .Net
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2007
    Messages : 823
    Par défaut
    Merci pour l'info.
    Comme le besoin n'existe plus, je vais remettre ça a plus tard.
    Mon nouveau besoin est de récupérer toutes les lignes ayant des champs tous les champs identiques parmi une liste de champs. de faire un max sur un champ de mettre cette valeur sur la première ligne et 0 sur les autres.

    C'est une correction de bug qui doit être validée avant d'être effectuée. Mais, avec ce que tu m'as fourni, je devrais pouvoir m'en tirer. Je manque de pratique sur LinQ...
    Pour le comparer, j'ai remplacé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (_excludedColumnIndexes.Contains(i)) continue;
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (!_excludedColumnIndexes.Contains(i)) continue;
    Cela devrait me donner afin de passer une liste de champs à comparer plutôt qu'une liste de champs à exclure (en passant la-dite liste)

  9. #9
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par kheironn Voir le message
    C'est une correction de bug qui doit être validée avant d'être effectuée. Mais, avec ce que tu m'as fourni, je devrais pouvoir m'en tirer. Je manque de pratique sur LinQ...
    Pour le comparer, j'ai remplacé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (_excludedColumnIndexes.Contains(i)) continue;
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (!_excludedColumnIndexes.Contains(i)) continue;
    Cela devrait me donner afin de passer une liste de champs à comparer plutôt qu'une liste de champs à exclure (en passant la-dite liste)
    Bah dans ce cas renomme excludedColumnIndexes en includedColumnIndexes, sinon quand tu vas revenir sur ce code dans quelques mois, tu vas rien comprendre

  10. #10
    Membre émérite Avatar de kheironn
    Homme Profil pro
    Chef de projets technique C# / MVC / .Net
    Inscrit en
    Février 2007
    Messages
    823
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projets technique C# / MVC / .Net
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2007
    Messages : 823
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Bah dans ce cas renomme excludedColumnIndexes en includedColumnIndexes, sinon quand tu vas revenir sur ce code dans quelques mois, tu vas rien comprendre
    Mauvais copier-coller

    la classe devient
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    internal class DataRowComparer : IEqualityComparer<DataRow>
        {
            private readonly int[] _sameColumnIndexes;
            /// <summary>
            /// Initialise une nouvelle instance de DataRowComparer à partir d'un tableau d'index de colonnes identiques.
            /// </summary>
            /// <param name="sameColumnIndexes">tableau d'index des colonnes identiques</param>
            public DataRowComparer(params int[] sameColumnIndexes)
            {
                _sameColumnIndexes = sameColumnIndexes;
            }
     
            public bool Equals(DataRow x, DataRow y)
            {
                for(int i = 0; i < x.Table.Columns.Count; i++)
                {
                    if(!_sameColumnIndexes.Contains(i))
                        continue;
                    if(!Equals(x[i], y[i]))
                        return false;
                }
                return true;
            }
     
            public int GetHashCode(DataRow row)
            {
                unchecked // Overflow is fine, just wrap
                {
                    int hash = 17;
                    for(int i = 0; i < row.Table.Columns.Count; i++)
                    {
                        if(!_sameColumnIndexes.Contains(i))
                            continue;
                        object fieldValue = row[i];
                        int fieldHash = fieldValue != null ? fieldValue.GetHashCode() : 0;
                        hash = hash * 23 + fieldHash;
                    }
                    return hash;
                }
            }
        }

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Remplacer une valeur dans un tableau
    Par msahmi dans le forum Tcl/Tk
    Réponses: 1
    Dernier message: 23/03/2008, 00h12
  2. Remplace une valeur dans une colonne
    Par roger34 dans le forum Langage SQL
    Réponses: 1
    Dernier message: 22/06/2007, 23h44
  3. Réponses: 5
    Dernier message: 12/01/2007, 22h40
  4. [MS SQL] Remplacer des valeurs dans plusieurs tables
    Par salmoliv dans le forum Langage SQL
    Réponses: 3
    Dernier message: 04/10/2006, 18h31
  5. [Tableaux] Remplacement de valeurs dans un texte
    Par JavaSearch dans le forum Langage
    Réponses: 7
    Dernier message: 17/01/2006, 00h33

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo