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

Macros et VBA Excel Discussion :

[VBA-E]Gestion de tableaux


Sujet :

Macros et VBA Excel

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    932
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 932
    Points : 448
    Points
    448
    Par défaut [VBA-E]Gestion de tableaux
    coucou,

    Je voudrais creer un tableau mais dont la taille peut etre modifiée.
    Je pensais que la syntaxe etait : Dim TabNom() As String

    mais lorsque je veux y mettre une valeur :
    TabNom(1) = "blabla"

    erreur : l'indice n'appartient pas à la selection


    Autre question, si je veux boucler sur tous les elements du tableau le code est bien : For Each i In TabNom()
    (je ne peux pas trop regarder à cause du souci precedent )

    Merci de vos réponses

  2. #2
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    932
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 932
    Points : 448
    Points
    448
    Par défaut
    Yop, (j'adore les monologues )

    Non mais tant que j'y suis, j'ai une autre question pratique à demander aux pros

    Je remplis 1 tableau comme je l'ai dit, mais j'aurais besoin de l'utiliser dans plusieurs Sub.

    Je voudrais biensur eviter de le declarer et de le remplir 2 fois...

    Je pensais faire une fonction du type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Function tableau (byval num as integer) as string
    dans laquelle je declare mon talbeau et le remplis. puis je retourne NomTab(num).

    Est ce que ca semble une bonne idée? Y'a t'il une meilleur solution pour ce cas?

    j'attends vos propositions

  3. #3
    Membre éclairé
    Avatar de Theocourant
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    618
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 618
    Points : 739
    Points
    739
    Par défaut
    Citation Envoyé par Elstak
    coucou,

    Je voudrais creer un tableau mais dont la taille peut etre modifiée.
    Je pensais que la syntaxe etait : Dim TabNom() As String

    mais lorsque je veux y mettre une valeur :
    TabNom(1) = "blabla"

    erreur : l'indice n'appartient pas à la selection


    Autre question, si je veux boucler sur tous les elements du tableau le code est bien : For Each i In TabNom()
    (je ne peux pas trop regarder à cause du souci precedent )

    Merci de vos réponses
    Salut,

    Alors pour les tableaux dynamiques, il faut leur donner qu'en même un dimension au début de ton appli mais pas dans la partie déclaration...

    Un exemple sera plus parlant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Dim monTab() as Integer
    Dim maMat() as Double
     
      ReDim monTab(2)
     
      'Et donc me voilà avec monTab avec une dimension de 2 valeurs
     
      monTab(1)=5
     
      ReDim Preserve monTab(6)
      'Et maintenant monTab à 6 valeurs mais j'ai gardé la valeur que je lui avait affecté ("Preserve")
     
      ReDim maMat(5,3)
      'Et voilà maMat à 2 dimensions de 5*3 valeurs... etc
    Enfin si tu as d'autres question, regarde l'aide en ligne de VB...

    +

    Théo

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    932
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 932
    Points : 448
    Points
    448
    Par défaut
    Ok,
    j'avais deja vu le redim mais je pensais qu'il existait une autre maniere pour qu'il trouve sa taille en fonction du nom d'elements qu'il contient.

    en fait là le redim me sert à rien car j'ai juste à definir la taille au debut...

    domage.

    Je suis toujours preneur pour des réponses à mon second post

  5. #5
    Membre éclairé
    Avatar de Theocourant
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    618
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 618
    Points : 739
    Points
    739
    Par défaut
    Citation Envoyé par Elstak
    Ok,
    j'avais deja vu le redim mais je pensais qu'il existait une autre maniere pour qu'il trouve sa taille en fonction du nom d'elements qu'il contient.

    en fait là le redim me sert à rien car j'ai juste à definir la taille au debut...

    domage.

    Je suis toujours preneur pour des réponses à mon second post
    Je comprends pas ce que tu veux dire ????
    Je crois que tu n'as pas compris ce que sont les tableaux dynamiques.

    La taille d'un tableau dynamique est définie lors de l'exécution du programme.

    Sinon pour avoir la taille si tu ne la connais pas, tu utilises Ubound et Lbound et là je te renvoie à l'aide en ligne.

    +

    Théo

  6. #6
    Inactif  
    Avatar de ouskel'n'or
    Profil pro
    Inscrit en
    Février 2005
    Messages
    12 464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 12 464
    Points : 15 543
    Points
    15 543
    Par défaut
    Hello Théo,

    Je crois qu'en fait Erzats a besoin de savoir que la dimension fixée par Redim preserve peut être une variable

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    Sub RedimPreserve()
    Dim MonTab(), i, dimension
        For i = 1 To 8 'Val(Quelquechose)
          ReDim Preserve MonTab(i)
          MonTab(i) = "tintin" & Str(i)
        Next
        dimension = UBound(MonTab)
        MsgBox dimension
    End Sub
    A tout hasard

    A+

  7. #7
    Modérateur
    Avatar de AlainTech
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Mai 2005
    Messages
    4 235
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 70
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Finance

    Informations forums :
    Inscription : Mai 2005
    Messages : 4 235
    Points : 24 327
    Points
    24 327
    Par défaut
    Citation Envoyé par Elstak
    en fait là le redim me sert à rien car j'ai juste à definir la taille au debut...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Dim TabNom(1) As String
    Te crée un tableau à 2 éléments ayant pour indices 0 et 1.

    Si tu préfères que tous tes tableaux commencent à l'indice 1, il faudra écrire dans la partie déclarations.

    Si tu veux avoir plus de souplesse concernant la limite inférieure, tu peux aussi déclarer comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Dim TabNom(1 To 2) As String

  8. #8
    Inactif  
    Avatar de ouskel'n'or
    Profil pro
    Inscrit en
    Février 2005
    Messages
    12 464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 12 464
    Points : 15 543
    Points
    15 543
    Par défaut
    L'union fait la force ! Aussi, grâce à jmfmarques qui a pensé à utiliser Let, voici la solution avé la founectiônne...
    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
     
    Sub Tableau() 
    Dim NbIndices, i, LeTab
    NbIndices = 5
    Let LeTab = FabriquerTableau(NbIndices)
        For i = 1 To NbIndices
            MsgBox LeTab(i)
        Next
    End Sub
     
    Function FabriquerTableau(Num)
    Dim LeTab
    ReDim LeTab(Num)
    For i = 1 To Num
       LeTab(i) = i
    Next
    FabriquerTableau = LeTab
    End Function
    N'oublie pas de nous dire...

    A+

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    932
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 932
    Points : 448
    Points
    448
    Par défaut
    Oulala, je vois à peu pres ce que tu veux dire mais je vais mieux reexpliquer ce que je veux car j'ai un peu avancé (jai meme reussi à faire ce que je voulais mais c'est pas génial...)

    Peut etre que ce n'est pas la meilleur maniere pour faire ca mais à part avec des tableaux je ne vois pas comment faire..

    J'ai 2 types de fichiers textes. J'ai un traitement particulier pour chaque type donc j'ai créé 2 tableaux. Je souhaite les convertirs en fichiers excel donc j'ai recréé 2 tableaux avec les noms de sortie. (Pour chaque tableau j'ai une fonction qui me renvoit le nom du fichier, je fourni l'indice pour appeler la fonction).

    Le petit probleme est que le nombre de fichiers peut augmenter (ou diminuer) donc je trouve ma solution un peu "rigide". voici ce que j'ai pour le moment :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    Public Function RetourneNomOrig(ByRef Num As Integer) As String
     
    Dim TabNom(1 To 100) As String
     
        TabNom(1) = "Nom1.txt"
        TabNom(2) = "Nom2.txt"        
     
    RetourneNomOrig = TabNom(Num)
     
    End Function
    j'ai mis 100 au maximum pour ne pas avoir à rajouter 1 à chaque ajout de fichier.

    j'ai aussi une seconde fonction comme ca pour le second type de fichier texte et 2 du meme genre pour le nom de fichier excel qui correspond (par exemple dans ma fonction retournenomsortie, Tabsortie(1) correspond au nom du fichier excel qu'a Nom1.txt apres convertion). Si j'ai choisi de faire une fonction c'est parceque j'ai besoin de ces noms dans plusieurs procédures indépendantes donc ca m'evite de tout avoir à redeclarer...

    Citation Envoyé par ouskel
    Aussi, grâce à jmfmarques qui a pensé à utiliser Let
    Je dors encore ou jmf n'a pas posté de message ??

    Voilà j'ai essayé de voir le Ubound que theo m'a conseillé mais je comprend pas trop en quoi il peut m'aider.

  10. #10
    Expert éminent sénior


    Profil pro
    Inscrit en
    Juin 2003
    Messages
    14 008
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 14 008
    Points : 20 040
    Points
    20 040
    Par défaut
    ton tableau ...? tu ne l'utilise et ne le rempli que dans la fonction ?

    si c'est le cas voir l'instruction Select Case...

  11. #11
    Inactif  
    Avatar de ouskel'n'or
    Profil pro
    Inscrit en
    Février 2005
    Messages
    12 464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 12 464
    Points : 15 543
    Points
    15 543
    Par défaut
    Si c'est ubound qui te gène... Ubound(Tableau) te sert à connaître la dimension du tableau... T'as pas F1 ?
    Ça sert... chaque fois que tu en as besoin...

    Pour ton pb, je n'aurais jamais fait comme toi... Tant qu'à travailler avec des fichiers textes, j'aurais continué avec des fichiers textes.
    En dehors de ces concidérations, je ne comprends absolument pas ce que tu demandes à ta fonction.

    Peut-être qu'une petite explication point par point qui nous évite de relire tous les messages depuis le 21 avril...

    A+

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    932
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 932
    Points : 448
    Points
    448
    Par défaut
    Yup,

    Pour le "ubound" j'ai utilisé F1, ce que je voulais dire c'est que je voit pas en quoi il peut m'aider

    ensuite il faut absolument que je convertisse les fichier textes en excel (pour utiliser les données apres, faire des calculs...) c'est dailleurs le but là !

    en gros, les fichiers texte je dois les telecharger, dont j'ai le nom dans ma fonction, j'appele pour i de 1 à mon nombre de fichier (ici j'ai for i=1 to 37) la focntion qui me les telecharge, ensuite dans une autre procedure, j'utilise de nouveau la fonction ('jai en fait dans la fonction Tab(1)=Nom1) pour avoir le nom et je lui ajoute .txt pour qu'il me l'ouvre et convertisse en fichier excel (les nom excel qui doivent etre donnés ne correspondent pas à Nom1.xls... d'où une autre fonction qui a les noms qui douivent etre donnés)

    j'appele donc ma fonction1 pour avoir le nom du fichier à ouvrir puis la 2 pour trouver le nom du fichier excel. (Tout ca en double, car je rapelle j'ai 2 type de fichiers qui ne sont pas convertis de la meme maniere)

    Donc pour un ajout d'un fichier à telecharger (qui sera ensuite converti) je doit changer dans ma procedure ma boucle (for i=1 to 38) puis pareil dans ma proceduer de convertion, c'est ca que je trouve assez lourd.

    Ma fonction ne contient que le nom des fichier (que le tableau)
    (Jvais voir pour ton idée bbil )

    Ptite info pour toi qui suis tous mes post ouskel, ce sont les fichier etxte qui ont les info : Nom;chiffre1;chiffre2 .... si ca te rapelle quelquechose

  13. #13
    Inactif  
    Avatar de ouskel'n'or
    Profil pro
    Inscrit en
    Février 2005
    Messages
    12 464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 12 464
    Points : 15 543
    Points
    15 543
    Par défaut
    Oui, si les séparateurs sont des ";" ça me rappelle un csv qui aurait l'extension txt. Tu changes l'extension et tu as un csv qu'excel peut ouvrir sans pb... Ensuite, tu peux travailler sur des sheets Excel. Donc, plus de txt, sauf pour celui qui a des virgules comme séparateurs. Ce serait lui qui m'embêterait le plus...
    Mais c'est juste une idée... A toi de voir ce que tu en fais...

    A+

  14. #14
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    932
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 932
    Points : 448
    Points
    448
    Par défaut
    Je te rapelle que pour les ";" il a quand meme fallu un petit code pour que la convertion fonctionne sans souci.

    donc j'ai bien besoin des 2 convertion et meme si il fallait juste les renommer, j'aurais besoin du tableau qui contient les noms excel !

    ensuite mon probleme, que je rapelle, est que je cherche à éviter de devoir changer, à chaque ajout ou suppression, ma boucle (mon for i=1 to 37).

    J'avais tenté un truc du genre il me semble mais ca n'allait pas

  15. #15
    Inactif  
    Avatar de ouskel'n'or
    Profil pro
    Inscrit en
    Février 2005
    Messages
    12 464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 12 464
    Points : 15 543
    Points
    15 543
    Par défaut
    Désolé mais je me rappelle pu ... et je ne comprends plus rien
    Et si tu résumais depuis le début.
    Tu pourrais par exemple nous expliquer, en bon vieux françois, ce que tu veux faire, en partant de quoi pour arriver à quoi...
    De post en post, pour ma part j'ai perdu le fil.
    Sauf si bbil ou AlainTech ont suivi jusque là, auquel cas, je passe la main.

    A+

  16. #16
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    932
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 932
    Points : 448
    Points
    448
    Par défaut
    hihi, t'as vraiment une mémoire de dodo

    Bon je récapitule :

    Je dois : -telecharger des fichiers dont j'ai le nom (fichiers texte)
    -les covnertir en fichier excel (avec un nom différents)
    -faire différents calculs (mais ca on s'en fou c'est pas le pb )

    Je vais faire tres long.

    J'ai créé un userform avec un bouton telecharger et un autre convertir.
    en cliquant sur telecharger, il telecharge les fichiers (jusque là rien de surprenant ) et me les fou dans le dosssier voulu.

    ensuite je dois reprendre 1 à 1 les fichier texte, les mettre en bonne forme sous excel et renommer les fichiers excel.

    D'où : -1 tableau avec le nom des fichiers texte
    -1 tableau avec le nom des fichiers excel

    exemple :
    TabTexte(1)="Nom1.txt"

    TabExcel(1)="Nomexcel.xls"

    chaque tableau dans 1 fonction.

    pour le telechargement j'utilise le premier (j'appele la 1ere fonction) . pour la convertion j'utilise les 2. c'est pourquoi j'ai mis ces tableau dans une fonction, pour ne pas avoir à remettre le tableau des txt dans la procedure de convertion. j'appele en realité, pour la convertion, une procedure qui importe le fichier texte (Tabtext(i)) et converti puis ue autre qui exporte (energistre la fichier converti sous le nom TabExcel(i))

    voilà (le probleme est quand meme simplifié là car en réalité c'est un peu plus long et compliqué mais si tu veux tous les détails, je les donnerais mais prevoit 1 heure de lecture ce soir avant de dormir )

  17. #17
    Expert éminent sénior


    Profil pro
    Inscrit en
    Juin 2003
    Messages
    14 008
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 14 008
    Points : 20 040
    Points
    20 040
    Par défaut
    bon pour en revenir à ton probléme de tableau ... tu as essayé select case ..?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    Public Function RetourneNomOrig(ByRef Num As Integer) As String
     
    Select Case Num
     
        Case 1 
            RetourneNomOrig =  "Nom1.txt"
     
        Case 2 
            RetourneNomOrig =  "Nom2.txt"
        Case Else
            RetourneNomOrig = "NomOriginNumero-" & Num & "-Inconnu.txt"
     End Select 
     End Function

  18. #18
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    932
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 932
    Points : 448
    Points
    448
    Par défaut
    Beuh non bbil...

    en gros le "case" me sert à eviter de definir le tableau si je comprend bien ?

    C'est pas si bete !

    Mais, là encore dans la fonction ca m'ennuye que moyennement. Le plus gros probleme est ma boucle dans mes 2 procédure (pour appeler la fonction) mon sacré "For i=1 to 37" qu'il faut modifier à la main.

    Une idée là dessus ??

    ensuite l'appel de la fonction pour récupérer le nom est Ok avec ta solution. Il y a juste à ajouter un cas si on ajoute 1 fichier. Par contre si on en retire 1, j'aurai une erreur dans ma boucle lorsque i vaudra la valeur manquanet non? a moins que j'ajoute une condition... mais ce serait quoi? si le nom renvoié par la fonction ="" alors on fait rien et on passe au i suivant ? enfin ca je verrai cet aprem
    Là ce que je cherche surtout est de regler le petit souci du for i= .....


    Ps: je vien de repenser à ca... au pire je met une grosse boucle du type for i=1 to 500 et j'utilise la condition que j'ai dite au dessus pour passer les retours de fonction vide... mais c'est pas super pro comme solution ca

  19. #19
    Inactif  
    Avatar de ouskel'n'or
    Profil pro
    Inscrit en
    Février 2005
    Messages
    12 464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 12 464
    Points : 15 543
    Points
    15 543
    Par défaut
    Le plus gros probleme est ma boucle dans mes 2 procédure (pour appeler la fonction) mon sacré "For i=1 to 37" qu'il faut modifier à la main
    Citation Envoyé par Tu
    D'où : -1 tableau avec le nom des fichiers texte
    -1 tableau avec le nom des fichiers excel
    Tu sembles stocker le nom de tes fichiers dans un tableau... Me trompe-je ?
    Si c'est le cas, Pierre Fauconnier t'as donné la solution hier :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    NbreDeFichiers = Ubound(TonTableau)
    For i = NbreDeFichiers  ' ou Ubound(TonTableau)
        ' Tu fais ce que tu as à faire
    Next
    Où est le pb ?

    A+

  20. #20
    Expert confirmé

    Profil pro
    Inscrit en
    Mai 2005
    Messages
    3 419
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 3 419
    Points : 4 297
    Points
    4 297
    Par défaut
    tu devrais utiliser des collections
    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
     
    'définition 
    'au niveau module
     
    Public macoll As New Collection
     
    'initialisation
    Sub initfichier()
    macoll.Add ("fichier1")
    macoll.Add ("autre fichier")
    End Sub
     
     
    Function existe(nomcherch As String) As Boolean
    'démonstration de for ach
    Dim temp As Boolean
    Dim parcourt As Variant
    For Each parcourt In macoll
          If parcourt = nomcherch Then temp = True
    Next parcourt
    existe = temp
    'démo d'ajout dynamique
    macoll.Add(inputbox("entrez un nom")
    End Function
     
    Function dnom(index As Integer) As String
    'renvoi d'un index
    dnom = macoll(index)
    End Function

Discussions similaires

  1. [VBA-E]Additionner des tableaux (matrices)
    Par Mut dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 07/04/2006, 20h18
  2. [VBA-E] Gestion d'une erreur #N/A
    Par Mut dans le forum Macros et VBA Excel
    Réponses: 9
    Dernier message: 07/04/2006, 20h17
  3. [VBA-E]Gestion d'erreur
    Par Mut dans le forum Macros et VBA Excel
    Réponses: 14
    Dernier message: 07/04/2006, 20h05
  4. [VBA-E] gestion des fichiers ouverts ...
    Par SpaceFrog dans le forum Macros et VBA Excel
    Réponses: 24
    Dernier message: 20/01/2006, 17h10
  5. Réponses: 5
    Dernier message: 04/04/2003, 15h02

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