Requête linq vers double -> exception cast non valide

Résolu
VbNicoG Messages postés 127 Date d'inscription vendredi 27 juin 2008 Statut Membre Dernière intervention 6 septembre 2019 - 25 févr. 2015 à 16:12
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 - 5 mars 2015 à 13:38
Bonjour,

Je récupère les valeurs d'une colonne dans un tableau d'objet en utilisant une requête linq dans un dataset "ds", voici le code :
Dim aTime() As Object
aTime = (From myRow In ds.Tables(0).AsEnumerable Select myRow.Field(Of Object)(index)).ToArray

Ceci fonctionne très bien. J'aimerais à présent récupérer ces mêmes valeurs directement dans un tableau de type double.
Dim aTime() As double
aTime = (From myRow In ds.Tables(0).AsEnumerable Select myRow.Field(Of double)(index)).ToArray

Une exception est levée "le cast spécifié n'est pas valide". Comment puis-je convertir directement ces données en tableau de double ? sans faire une boucle où l'on ferait la conversion pour chaque valeur une par une ;)

Merci
A voir également:

14 réponses

ucfoutu Messages postés 18038 Date d'inscription lundi 7 décembre 2009 Statut Modérateur Dernière intervention 11 avril 2018 211
25 févr. 2015 à 17:07
Bonjour,
je ne vois aucune colonne nommée.
Essaye (pour voir) ===>>
Dim aTime As Double() = (From myRow In ds.Tables(0).AsEnumerable Select myRow.Field(Of Double)("nom_de_la_colonne")).ToArray

en remplaçant nom-de_la_colonne par le nom de la colonne concernée

Sans "garantie du gouvernement" (je ne suis pas vb.nettiste).
Il se peut que ce que j'ai écrit nécessite un ";" in fine.
0
VbNicoG Messages postés 127 Date d'inscription vendredi 27 juin 2008 Statut Membre Dernière intervention 6 septembre 2019
25 févr. 2015 à 17:20
Bonjour,

Merci de ton message.

En fait j'ai une variable integer "index" à la place du nom de la colonne. Elle définit le numéro de la colonne, ça marche aussi bien qu'avec le nom de la colonne.

Donc le problème est le même.

En fait, il est possible que j'ai des cellules nulles ou de type très variées dans cette colonne et je pense que l'exception de conversion vers le type double vient de là.
Il faudrait que j'arrive à ce que les données qui ne sont pas convertibles en double ne soient pas prises en compte. C'est une piste mais peut être que je me plante...
0
ucfoutu Messages postés 18038 Date d'inscription lundi 7 décembre 2009 Statut Modérateur Dernière intervention 11 avril 2018 211
25 févr. 2015 à 17:43
En fait, il est possible que j'ai des cellules nulles ou de type très variées dans cette colonne

Il est clair que tu ne peux alors en tirer un tableau de numériques.
1) Quels sont ces types variés ?
2) Qu'entends-tu par "cellules nulles" : des cellules vides ou des cellules contenant un Null (ce n'est pas la même chose)

Si la réponse en 1) est : des numériques et des chaînes de caractères (pouvant être vides, mais ne pas contenir un NULL) , uniquement (pas d'objets ni de booléens) ==>> tu peux en tirer un tableau de strings
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
Modifié par Whismeril le 25/02/2015 à 19:44
Bonsoir

As tu essayé
aTime = (From myRow In ds.Tables(0).AsEnumerable Select Convert.ToDouble(myRow.Field(Of objetc)(index))).ToArray
?

Modérer m'amène à intervenir dans de nombreux posts, mais les seuls langages que je connaisses sont le C# et un peu de VB. Pour vos codes pensez à la coloration.
Réponse trouvée ->Question Résolue
0

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

Posez votre question
VbNicoG Messages postés 127 Date d'inscription vendredi 27 juin 2008 Statut Membre Dernière intervention 6 septembre 2019
2 mars 2015 à 15:04
Bonjour,

Merci pour vos réponses.

Whismeril, avec ton code j'ai une autre exception "le format de la chaîne d'entrée n'est pas valide".

UcFoutu, effectivement je peux en tirer un tableau de string mais j'aimerais récupérer un tableau de double sans faire une boucle de conversion derrière.

Pour info, mon fichier de base est un fichier .txt qui a une multitude de chose avant d'arriver à un "tableau" de 478 par 120 000 dont les champs sont séparés par une virgule (similaire à un .csv). Avant, je traitais ces données ligne par ligne, champs par champs avec un "streamReader", il faut environ 30 secondes pour mettre en variables les données de ce tableau. En passant par la base de données OLEDB et les requêtes linq, j'ai l'impression (à confirmer...) que ce temps de traitement est nettement optimisé (à confirmer)! J'aimerais éviter de ruiner ces nouvelles performances avec une boucle interminable même si celle ci de devrait pas prendre tant de temps que ça...

Qu'en pensez vous ?
0
ucfoutu Messages postés 18038 Date d'inscription lundi 7 décembre 2009 Statut Modérateur Dernière intervention 11 avril 2018 211
2 mars 2015 à 17:40
Je ne suis pas certain d'avoir tout compris, sauf qu'il semble bien qu'il a là carence de modélisation des données.
As-tu essayé d'ajouter une clause Where pour ne garder que les données numériques de la colonne concernée par ce "mélange" ?
0
VbNicoG Messages postés 127 Date d'inscription vendredi 27 juin 2008 Statut Membre Dernière intervention 6 septembre 2019
2 mars 2015 à 17:59
Oui, j'ai ajouté une condition "where" afin de ne taper que dans les lignes où se trouvent les données mais le résultat est le même malheureusement.
Est ce que cela veut dire que la totalité du fichier est prise en compte puis que la clause "where" ne s'applique seulement par la suite et donc plante avant l'exécution du "where"?

Par contre, si je copie uniquement mon tableau dans un fichier .txt à part, ça fonctionne! C'est une bidouille que je n'aime pas trop...mais n'est-ce finalement pas la seule et unique solution ?
0
ucfoutu Messages postés 18038 Date d'inscription lundi 7 décembre 2009 Statut Modérateur Dernière intervention 11 avril 2018 211
2 mars 2015 à 18:03
montre ta requête avec la clause where
0
VbNicoG Messages postés 127 Date d'inscription vendredi 27 juin 2008 Statut Membre Dernière intervention 6 septembre 2019
2 mars 2015 à 18:30

Dim aTime() As double
aTime = (From myRow In ds.Tables(0).AsEnumerable Where myRow.Item(0).ToString.StartsWith("Record") Select myRow.Field(Of double)(0)).ToArray
0
ucfoutu Messages postés 18038 Date d'inscription lundi 7 décembre 2009 Statut Modérateur Dernière intervention 11 avril 2018 211
2 mars 2015 à 19:06
Je ne te comprends plus du tout.
ceci :
 ...Where myRow.Item(0).ToString.StartsWith("Record")...

tendrait à ne garder que ce qui commence par "Record"
Je ne vois alors pas comment des articles commençant par "record" pourraient être des doubles !
Explique.

0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
2 mars 2015 à 22:30
Bonsoir au final qu'as tu besoin de récupérer.
Une collection de 120 000 "lignes" de données contenant 478 "colonnes"?

Si oui tu peux peut être écrire une classe qui représente ta ligne, (pas avec 478 propriétés ça va être long) mais avec une liste ou un tableau pour les données de mêmes types. C'est d'ailleurs peut être ce que tu essaies de faire.

Ensuite tu peux essayer de lire ton fichier avec File.ReadAllLine, qui te retourne directement une liste ou un tableau.

Tu écris une méthode qui convertit ta ligne en instance de ta classe, c'est cette méthode que tu mets dans la requête.

Voilà un exemple, le fichier csv
Ligne 1;1;1;;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;E
Ligne 2;2;2;2;2;2;2;&;2;2;2;2;2;2;2;2;2;2;2;2;2
Ligne 3;3;3;3;3;3;C;3;3;3;Z;3;3;3;3;3;3;3;3;3;3
Ligne 4;4;F;4;4;4;4;4;4;4;4;4;4;4;4;4;4;4;4;E;4
Ligne 5;5;5;5;5;5;5;5;5;E;5;5;5;5;5;5;5;5;5;5;5
Ligne 6;6;6;6;six;6;6;6;6;6;6;6;6;6;6;6;6;6;6;6;6
Ligne 7;7;7;7;7;7;7;7;7;7;7;7;7;7;7;7;E;7;7;7;7
Ligne 8;8;8;8;8;8;8;8;8;-;8;8;8;8;8;8;8;8;8;8;8
Ligne 9;9;9;9;9;A;9;9;9;9;9;9;A;9;9;9;9;9;9;9;9
Ligne 10;R;10;10;10;10;10;10;;10;10;10;10;10;10;10;10;10;10;10;10


La classe
Imports System.Collections.Generic
Imports System.Linq

Public Class ExempleVBNicoG
    Public Sub New(ByVal Ligne As String)
        Dim valeurs() As String = Ligne.Split({";"c})
        Nom = valeurs(0)
        Donnees = valeurs.Skip(1).Select(Function(v) Conversion(v)).ToList() 'le skip saute le premier enregistrement qui est le nom le reste de la requette retourne une liste de double
    End Sub

    Public Property Nom() As String

    Public Property Donnees() As List(Of Double)

    'Convertit le texte en double si c'est possible, sinon retourne 0
    Private Function Conversion(ByVal Texte As String) As Double
        Dim nombre As Double
        If Double.TryParse(Texte, nombre) Then
            Return nombre
        Else
            Return 0
        End If
    End Function
End Class


et la requette de lecture du fichier
Imports System.IO
'.....
        Dim donneesVBNicoG As List(Of ExempleVBNicoG) = File.ReadAllLines("exemple.csv").Select(Function(l) New ExempleVBNicoG(l)).ToList()
0
VbNicoG Messages postés 127 Date d'inscription vendredi 27 juin 2008 Statut Membre Dernière intervention 6 septembre 2019
3 mars 2015 à 09:06
Bonjour,

Ucfoutu, c'est exactement ça, je ne veux récupérer les variables dans les colonnes pour lesquelles la ligne commence par le texte "Record" car avant j'ai du blabla comme je l'ai précisé avant. Le fichier ressemble à ceci :

blabla
blabla
blabla
Record1,0.01,10.2,0.21
Record2,0.234,11.2,33.23
Record3,0.456,12.2,31.2
Record4,0.901,13.2,43.1

Je t'épargne les 478 colonnes et 120 000 lignes. Je précise encore une fois que le code fonctionne pour des objets, c'est à dire que dans cet exemple je récupère les tableaux d'objets tab1, tab2, tab3 comme cela :

tab1(0) = 0.01
tab1(1) = 0.234
tab1(2) = 0.456
tab1(3) = 0.901

tab2(0) = 10.2
tab2(1) = 11.2
tab2(2) = 12.2
tab2(3) = 13.2

tab3(0) = 0.21
tab3(1) = 33.23
tab3(2) = 31.2
tab3(3) = 43.1

En utilisant le code suivant :

dim tab1() as object
dim tab2() as object
dim tab3() as object

tab1= (From myRow In ds.Tables(0).AsEnumerable Where myRow.Item(0).ToString.StartsWith("Record") Select myRow.Field(Of object)(1)).ToArray
tab2= (From myRow In ds.Tables(0).AsEnumerable Where myRow.Item(0).ToString.StartsWith("Record") Select myRow.Field(Of object)(2)).ToArray
tab3= (From myRow In ds.Tables(0).AsEnumerable Where myRow.Item(0).ToString.StartsWith("Record") Select myRow.Field(Of object)(3)).ToArray


Whismeril, ce que j'ai besoin c'est de récupérer un tableau de double pour chaque colonne dont la ligne commence par le texte "Record" donc 478 tableaux de double d'une taille de 120 000 chacun.

Si je comprends bien ton code, il permet de récupérer 120 000 tableaux d'une taille de 478 (variables donnees()). Par la suite, il faudra que je décompile les données pour les recompiler dans mes 478 tableaux d'une taille de 120 000. C'est exactement ce que je fais à l'heure actuelle, ce qui malheureusement prend 30 secondes. Mon but est de réduire ce temps au maximum, ce qui semble bien mieux fonctionner avec les requêtes linq mais j'ai besoin de tableau de double et non des tableaux d'objet.

Merci pour votre aide.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
3 mars 2015 à 09:40
Ok je comprends mieux.
Je tâche d'y regarder ce soir.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
4 mars 2015 à 07:55
Bonjour, pas trouvé mieux que

    Private Sub LectureFichierVBNicoG()
        Dim donneesVBNicoG = File.ReadAllLines("exemple.csv").Where(Function(l) l.StartsWith("OK")).Select(Function(l) l.Split({";"c}).Skip(1))

        Dim mesLites As List(Of List(Of Double)) = New List(Of List(Of Double))()

        For i = 0 To 20
            mesLites.Add(donneesVBNicoG.Select(Function(t) Conversion(t(i))).ToList())
        Next
    End Sub

    'Convertit le texte en double si c'est possible, sinon retourne 0
    Private Function Conversion(ByVal Texte As String) As Double
        Dim nombre As Double
        If Double.TryParse(Texte, nombre) Then
            Return nombre
        Else
            Return 0
        End If
    End Function


Pour ce fichier csv un peu modifié
OK;1;1;1;;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1
NOK;2;2;2;2;2;2;2;&;2;2;2;2;2;2;2;2;2;2;2;2;
NOK;;3;3;3;3;3;C;3;3;3;Z;3;3;3;;3;3;3;3;3;3
OK;4;4;F;4;4;4;4;4;4;4;4;4;4;quatre;4;4;4;l;4;E;4
OK;5;5;5;5;5;5;5;5;5;E;5;5;5;5;5;5;5;5;5;5;5
OK;6;6;6;6;six;6;6;6;6;6;6;6;6;6;6;6;6;6;6;6;6
NOK;7;7;7;7;7;7;7;7;7;7;7;7;7;7;7;7;E;7;7;7;7
OK;8;8;8;8;8;8;8;8;8;-;8;t;8;8;8;8;8;8;m;8;8
NOK;9;9;9;9;9;A;9;9;9;9;9;9;A;9;9;9;9;9;9;9;9
OK;10;R;10;10;10;10;10;10;;10;10;10;10;10;10;;10;10;10;10;10
0
VbNicoG Messages postés 127 Date d'inscription vendredi 27 juin 2008 Statut Membre Dernière intervention 6 septembre 2019
5 mars 2015 à 09:07
Bonjour Whismeril,

Merci de ton aide mais cette solution ne me convient pas.
J'ai trouvé une autre façon de faire, je traite mon fichier avec un StreamReader jusqu'à atteindre mon 'fameux' tableau. A ce moment là, je mets tout le reste du fichier en mémoire dans une variable 'string' avec la fonction ReadToEnd.
Je crée un nouveau fichier dans lequel je copie tout ce tableau et les requêtes linq fonctionne. En fait c'est tout le blabla avant le tableau qui posait un problème de cast.

Le temps de mise en mémoire et de 22 secondes au lieu de 30 secondes...je m'attendais à mieux mais bon.

Merci encore pour votre aide, j'estime le post 'résolu' sur ma réponse même si c'est loin d'être propre et parfait.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
5 mars 2015 à 13:38
Bonjour, tant pis si je ne t'ai pas aidé, et tant mieux si tu as trouvé un truc qui te vas.

Essaye de remplacer ton stremreader par File.ReadAllTexte, pour le cas d'un fichier sur un lecteur réseau ça marchait mieux.
0
Rejoignez-nous