Ranger un tableau

wallfredwallfred Membre
juin 2011 modifié dans Vos applications #1
Bonjour, :)

J'ai en gros problème et je n'arrive pas à  m'en sortir. Ca fait quelques jours que je suis dessus sans résultat.

Je recupere des données d'un serveur (qui me permettent d'établir un objet Evenement).
J'ai au final, un objet Evenement avec plusieurs caractéristiques (monEvenement.nom, monEvenement.lieu, monEvenement.dateHeure). Sachant que monEvenement.dateHeure peut être au format String ou NSDate.

Je veux récupérer ces évènements puis les afficher dans une table. Ca marche, j'arrive à  les afficher, mais pas comme je voudrais.
J'aimerais que l'utilisateur ait une table avec comme cellules, les différents jours des événements (donc le champ monEvenement.dateHeure). Et que lorsque l'on clique sur un jour précis, on a tous les événements associés.

En ce moment j'ai les jours (que je récupère avec monEvenement.dateHeure) dans une table.
Cependant il va me mettre 2 fois la même date dans ma table car il y aura 2 événements ce jour la. Et ça me pose problème.

J'ai essayé de créer un tableau de String en prenant juste les dates, et en évitant les doublons. Mais dans ce cas, je n'arrive pas à  récupérer ensuite le contenu de mes événements. Puisque je n'ai plus qu'un simple tableau.


Merci de votre aide.

Réponses

  • AliGatorAliGator Membre, Modérateur
    04:22 modifié #2
    Fais un tableau séparé qui liste toutes :
    - à  l'aide du KVC tu récupères en une ligne un NSArray contenant toutes les clés dateHeure de ton tableau événement
    - puis tu crées un NSSet à  partir de ce NSArray pour qu'il supprime de fait les doublons
    - puis tu retransforme en NSSet
    Enfin c'est la méthode la plus simple que je vois pour ça.
    NSArray* uniqueDates = [[NSSet setWithArray:[evenements valueForKey:@"dateHeure"]] allObjects];
    


    Voire même ne sortir que les dates sans les heures pour que les évènements le même jour soient groupés même s'ils sont à  des heures différentes (là  je prévoirais une méthode "date" dans la classe Evenement qui retourne cette date sans l'heure, ça facilitera le tri).

    Ensuite tu utilises ce tableau filtré pour afficher tes dates dans ta TableView.
    Et quand tu cliques sur une ligne de ta TableView, tu cherches dans ton tableau comlpet d'évènements ceux qui ont cette date.

    Autre solution, sans doute plus propre, c'est de transformer ton NSArray d'évènements en un NSDictionary contenant les évènements indexés par leur date. Pour la clé correspondant à  une date, tu as un NSArray contenant tous les évènements à  cette date.

    Solution même plus simple en terme d'intégration, c'est que ce soit ton WebService qui fasse ce travail et te retourne déjà  une structure organisée. A voir comment tu as prévu la conception de ton WebService et comment il est utilisé dans les reste de ton appli, etc.
  • wallfredwallfred Membre
    04:22 modifié #3
    Merci d'avoir répondu.

    J'ai déjà  essayé NSSet ça fonctionne que si je fait auparavant un nouveau tableau sans les heures , mais ça me mélange mes dates.
    Et je n'arrive pas à  les remettre dans l'ordre.

    En plus de ça, je suis bloqué pour afficher les événements qui contiennent la date. Parce que je prends la date, je la coupe pour la mettre dans un nouveaux tableau, puis je supprime les doublons en l'ajoutant encore dans un nouveau tableau.
    A la fin je me retrouve avec un tableau de String qui n'a plus aucun lien avec le contenu de mes évènements.

    Concernant la méthode des NSdictionnary, j'ai essayé mais je n'arrive pas à  associer plusieurs évenement à  une date.

    Pour la troisième solution, ce serait super lol, mais le serveur n'est pas à  moi. Et j'ai aucune possibilité de modifier ça.
  • AliGatorAliGator Membre, Modérateur
    04:22 modifié #4
    Bah avec NSSet oui c'est normal que ça change l'ordre, un NSSet est un ensemble (au sens mathématique), donc ce n'est pas ordonné. Quand tu reconstruis un NSArray ensuite à  partir du NSSet en lui demandant "allObjects", tu peux toujours ensuite retrier ce tableau avec sortedArrayUsingDescriptor etc, bref comme tu ferais pour trier un NSArray classique.

    Pour le lien avec les événements, suffit de faire une comparaison.
    Pour le NSDictionary, suffit d'associer un NSArray d'events

    Bref ce n'est que de la manipulation de données. A toi de voir ensuite comment tu veux organiser ton modèle de ton MVC.
  • wallfredwallfred Membre
    04:22 modifié #5
    Okay, pour NSSet, oui et après je trie, j'essayerai de faire ça.
    En ce moment j'arrive à  récupérer mes dates sans doublons dans un tableau (j'ai réussi avec des boucles for, ce n'est pas trés efficace, mais je vais m'orienter vers les NSSet).

    Je les affiches dans le titleForHeaderInSection: de ma tableView. Mais j'ai toujours le même soucis, comment je vais associer chaque cellule à  la bonne date vu qu'il peut y avoir plusieurs évènements dans une journée.

    C'est la que je comprend pas, je vois pas comment a partir d'un tableau de date, je vais réussir à  récupérer le bon évènement puis afficher son nom (qui est dans un autre tableau).
    En sachant que le lien entre mes 2 tableaux est inexistant (enfin pour moi, et je pense que c'est la que je dois faire fausse route). Mon premier tableau contient mes événements avec les informations et des dates avec l'heure en format String. Mon Second tableau contient ces mêmes dates (sans l'heure) en format string aussi. Peut-ton relier ces 2 tableaux d'une façon ?



    Merci
  • laudemalaudema Membre
    juin 2011 modifié #6
    Il y a peut être distinctUnionOfObjects qui pourrait servir
    J'ai pour ma part un tableau (corps) qui contient des dictionnaires dont une des clefs est "Code" et la valeur une NSString. J'obtiens les différents "Code" dans l'ordre du tableau avec la ligne
    <br />NSArray *distinctCodes = [corps valueForKeyPath:@&quot;@distinctUnionOfObjects.Code&quot;];<br />
    

    ça marche aussi un peu plus loin avec une NSString "Date"..
    ça ne me donne pas les dictionnaires, juste un tableau des différents objets, pour trouver les dictionnaires (jusqu'à  maintenant dans l'ordre du tableau aussi) j'utilise un NSPredicate et une boucle sur le tableau des valeurs uniques.
    Si ça peut aider ...
  • wallfredwallfred Membre
    04:22 modifié #7
    Merci beaucoup dd ton aide.

    Mais je ne vois toujours pas comment procéder.
    Mes tableaux sont :
    1. Un tableau avec des dates en string sans les heures
    2. Un tableau contenant les objets évènements avec les dates, nom, lieu (dates en string mais avec l'heure en plus)

    Comment à  partir de mon tableau1, je peux ressortir des valeurs du tableau2 à  un champ correspondant ?
  • Eddy58Eddy58 Membre
    04:22 modifié #8
    dans 1307459021:

    En sachant que le lien entre mes 2 tableaux est inexistant (enfin pour moi, et je pense que c'est la que je dois faire fausse route). Mon premier tableau contient mes événements avec les informations et des dates avec l'heure en format String. Mon Second tableau contient ces mêmes dates (sans l'heure) en format string aussi. Peut-ton relier ces 2 tableaux d'une façon ?

    Quand tu sélectionnes une date dans ton tableau 1, pourquoi ne fais tu pas un filtrage avec les données peuplant ton tableau 2 qui ne te ressortirait que les objets ayant la même date que celle sélectionnée ?
  • laudemalaudema Membre
    juin 2011 modifié #9
    dans 1307516386:

    Merci beaucoup dd ton aide.

    Mais je ne vois toujours pas comment procéder.
    Mes tableaux sont :
    1. Un tableau avec des dates en string sans les heures
    2. Un tableau contenant les objets évènements avec les dates, nom, lieu (dates en string mais avec l'heure en plus)

    Comment à  partir de mon tableau1, je peux ressortir des valeurs du tableau2 à  un champ correspondant ?

    [Edit]
    Désolé, j'avais mal lu: si ton tableau evenements a des dates+heure en string tu n'as qu'à  faire un substringToIndex:indexFinDate pour en sortir la date sans heure.
    // indexFinDate selon le format de tes strings du tableau 1
    Puis
    [/edit]
    Une boucle (for NSString *stringDate in lesChainesTableau1)
    {
    [tableau2 IndexesOfObjectsPassingTest:^Bloc {return [[[obj date] substringToIndex:indexFinDate] isEqualToString:stringDate]}] //obj est l'élément du tableauEvenements en train de passer le test
    }
    A la sortie tu as un indexSet que tu utilises avec objectsAtIndexes
    hth
  • wallfredwallfred Membre
    juin 2011 modifié #10
    Merci beaucoup laudema, je vais essayer de comprendre ce que tu a écrit..

    Sinon de mon coté, J'ai fait quelques chose qui fonctionne. Mais le problème c'est que mon application rame vraiment beaucoup.

    Dans mon avancement, j'ai réussi à  ressortir un tableau avec les dates sans doublons, je me sert de ce tableau dans "numberOfRowsInSection:" pour definir une date = une section :
    <br />- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section<br />{<br />&nbsp; &nbsp; //Recupere le nom des événements à  chaque dates<br />&nbsp; &nbsp; NSMutableArray*tableauDeNom=[[NSMutableArray alloc] init];<br />&nbsp; &nbsp; int count=0;<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; for (int i=0; i&lt;[tableau count]; i++) {<br />&nbsp; &nbsp; &nbsp; &nbsp; Evenement *unEvenement = [tableau objectAtIndex:i];<br />&nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];<br />&nbsp; &nbsp; &nbsp; &nbsp; [dateFormatter setDateFormat:@&quot;dd-MM-yyyy&quot;];<br />&nbsp; &nbsp; &nbsp; &nbsp; NSString *dateStr1 = [dateFormatter stringFromDate:unEvenement.dateHeure];<br />&nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; [dateFormatter release];<br />&nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; if ([dateStr1 isEqualToString:[tableauDeDate objectAtIndex:section]]) {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [tableauDeNom addObject:unEvenement.nom];<br />&nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; count=[tableauDeNom count];<br />&nbsp; &nbsp; [tableauDeNom release];<br />&nbsp; &nbsp; return count;<br />}<br />
    




    Puis je m'en sert dans "cellForRowAtIndexPath", et je pense que c'est ici que sa rame :
    <br />- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath<br />{<br />&nbsp; &nbsp; static NSString *CellIdentifier = @&quot;Cell&quot;;<br />&nbsp; &nbsp; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];<br />&nbsp; &nbsp; if (cell == nil) {<br />&nbsp; &nbsp; &nbsp; &nbsp; cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;//pour avoir la petite flèche à  droite<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; //Recupere le nombre de cours à  chaque date<br />&nbsp; &nbsp; NSMutableArray*tableauDeNom=[[NSMutableArray alloc] init];<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; //Parcours le tableau<br />&nbsp; &nbsp; for (int i=0; i&lt;[tableau count]; i++) {<br />&nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; //Recupere juste la date<br />&nbsp; &nbsp; &nbsp; &nbsp; Evement *unEvement = [tableau objectAtIndex:i];<br />&nbsp; &nbsp; &nbsp; &nbsp; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init] ;<br />&nbsp; &nbsp; &nbsp; &nbsp; [dateFormatter setDateFormat:@&quot;dd-MM-yyyy&quot;];<br />&nbsp; &nbsp; &nbsp; &nbsp; NSString *dateStr2 = [dateFormatter stringFromDate:unEvement.dateHeure];<br />&nbsp; &nbsp; &nbsp; &nbsp; [dateFormatter release];<br />&nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; //On va rechercher les noms pour une date précise<br />&nbsp; &nbsp; &nbsp; &nbsp; if ([dateStr2 isEqualToString:[tableauDeDate objectAtIndex:indexPath.section]]) {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [tableauDeNom addObject:unEvement.nomEvement;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br /><br />&nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; <br />	cell.textLabel.text=[tableauDeNom objectAtIndex:indexPath.row];<br />&nbsp; &nbsp; [tableauDeNom release];<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; return cell;<br />}<br />
    


    Est-ce que la lenteur est due aux cellules qui à  chaque fois exécutent la boucle for?
  • AliGatorAliGator Membre, Modérateur
    04:22 modifié #11
    Y'a des chances oui.

    Ne jamais faire de calculs complexes dans les méthodes cellForRow & numberOfRows ou numberOfSections. Ce sont des méthodes qui sont appellées plein de fois (à  chaque fois qu'une cellule doit s'afficher à  l'écran, y compris donc à  chaque fois que tu scroll ta TableView).

    Il faut calculer tes correspondances avant.
    D'où l'idée que je t'avais suggéré de construire un NSDictionary, dont les clés sont tes dates (sans les heures), et les valeurs associées à  chaque clé sont tes NSArray "tableauDeNom" correspondant. Comme ça à  tout moment tu peux récupérer tous les événements associés à  une date donnée en demandant au dictionaire la valeur (qui sera un NSArray d'évènements donc) associée à  la date.

    Dans numberOfSections il suffira de retourner le nombre de clés de ton dictionnaire (nombre de dates)
    Dans titleForSection de retourner le nom de la clé donnée
    Dans numberOfRowsForSection de retourner le nombre d'éléments du NSArray associé à  la date de la section
    etc.
  • wallfredwallfred Membre
    04:22 modifié #12

    Faut que je refasse tout alors.
    Mais je vois pas comment le faire avant, vu que justement j'avais "section" qui me permettait de tout delimiter et de bien trier.
    Si je rend "section" commun à  toute la classe (je le déclare dans le .h), est que je fait exactement les mêmes méthodes dans le viewDidLoad, ça pourrait le faire ? non ?
  • laudemalaudema Membre
    04:22 modifié #13
    En plus de ce que dit Ali si tu dois créer, initialiser, formater à  chaque passage dans ta boucle un NSDateFormatter  pour le détruire en sortant; autant en faire une variable d'instance et la détruire dans le dealloc de la classe, il ne sera créé qu'une fois par instance. Tu pourrais même en faire une variable de classe si tu en as souvent besoin (auquel cas tu utilises + (void)initialize pour le créer et tu ne t'occupes pas de le supprimer).
    Ce qui donne
    <br />NSString *dString = [tableauDeDate objectAtIndex:section];<br /> for (id unEvenement in tableau) //La boucle for permet l&#039;énumération rapide d&#039;un tableau d&#039;objets cf NSFastEnumeration<br />{ if ([ [monDateFormater stringFromDate:unEvenement.dateHeure] isEqualToString:dString]<br />&nbsp;  [tableauDeNom addObject:unEvenement.nom] ;<br />}<br />
    

    Pour aller (probablement) plus vite dans ta boucle..
    A jouter aux améliorations de fond que suggère Ali, car lui utilises iOS pas moi et il vaut mieux que tu apprennes à  bien penser tes classes dès le début. amha

  • AliGatorAliGator Membre, Modérateur
    04:22 modifié #14
    Dès que tu as ton tableau de tous tes Evènements (lors de la réponse à  ton WebService j'imagine ?), tu réorganises ce tableau en un NSDictionary de NSArray d'Evenements, dictionnaire indexé par les dates. Puis tu appelles reloadData sur ta TableView.
    C'est à  ce moment là  (qd tu reçois ton tableau d'Evenements) qu'il faut le restructurer, pour avoir une structure adaptée à  ton UITableViewDataSource.
  • wallfredwallfred Membre
    juin 2011 modifié #15
    Merci, je vais essayer.
    J'ai déjà  mon tableau de dates, et un tableau d'evenement. Si j'ai bien compris, je prend la méthode de "laudema" :
    <br />NSString *dString = [tableauDeDate objectAtIndex:section];<br /> for (id unEvenement in tableau) //La boucle for permet l&#039;énumération rapide d&#039;un tableau d&#039;objets cf NSFastEnumeration<br />{ if ([ [monDateFormater stringFromDate:unEvenement.dateHeure] isEqualToString:dString]<br />&nbsp;  [tableauDeNom addObject:unEvenement.nom] ;<br />}<br />
    


    Ca permet de récupérer les nom événements à  une date particuliere, si 'ai bien compris.

    Ensuite, comme dit "Aligator", je rajoute un dictionnaire à  la fin, avec comme clé : date, nom (éventuellement lieu par la suite).
    C'est bien ça ? Et comment on va pourvoir ajouter deux unEvement.nom à  une même date ?
    J'ai essayé, et il me renvoi que le dernier nom (efface le premier)
    <br />[tableauAffichage setObject:unEvenement.nom forKey:dString];<br />
    

  • AliGatorAliGator Membre, Modérateur
    04:22 modifié #16
    Mais pourquoi diable créer un NSArray tableauDeNom au lieu de mettre directement les objets Evenement dans ce tableau ? Pourquoi ne garder que les noms ?
    La construction du dictionnaire n'est pourtant pas compliquée...

    Soit tabEvents ton NSArray contenant tous tes objets Evenements (par exemple variable locale que tu récupères dans la méthode qui reçoit la réponse à  ton WebService quand ce dernier te retourne tous tes événements en vrac) :
    NSMutableDictionary* indexedEvents = [[NSMutableDictionary alloc] init];<br />NSDateFormatter* df = [[NSDateFormatter alloc] init];<br />[df setDateFormat:@&quot;dd-MM-yyyy&quot;];<br /><br />for(Evenement e in tabEvents) {<br />&nbsp; // On récupère la chaà®ne qui ne représente que la date<br />&nbsp; NSString dateString = [df stringFromDate:unEvenement.dateHeure];<br />&nbsp; // on regarde s&#039;il&nbsp; y a déjà  une entrée pour cette date dans le dico, sinon on l&#039;ajoute<br />&nbsp; NSMutableArray* eventsForThatDate = [indexedEvents objectForKey:dateString];<br />&nbsp; if (eventsForThatDate == nil) {<br />&nbsp; &nbsp; eventsForThatDate = [NSMutableArray array]; // encore aucune entrée pour cette date, on initialise un tableau vide<br />&nbsp; &nbsp; [indexedEvents setObject:eventsForThatDate forKey:dateString]; // et on l&#039;ajoute à  l&#039;entrée du dico correspondante<br />&nbsp; }<br />&nbsp; // A ce stade eventsForThatDate est le NSMutableArray qui qui est associé à  la date dateString dans le dictionnaire<br />&nbsp; // et ce NSMutableArray va contenir tous les events à  ladite date<br />&nbsp; [eventsForThatDate addObject:e]; // donc on ajoute l&#039;évènement<br />}<br /><br />[df release];<br />self.eventsDict = indexedEvents;<br />[indexedEvents release];
    
    Avec ça dans la propriété eventsDict (que tu auras pris soin de déclarer dans ton .h en mode retain, cela va de soi) tu auras un dictionnaire indexé par les dates, et pour chaque date tu auras un tableau contenant tous les évènements qui sont à  cette date.

    A partir de là , maintenant que ce dictionnaire de tableaux d'évènements est construit, cellForRowAtIndexPath et numberOfSections ou numberOfRowsInSection s'écrivent tout seul et ne prennent que quelques lignes.

    Bon sauf que là  je t'ai mâché le travail et même donné le code tout cru pour construire ce dictionnaire, tu n'as pas réussi à  le construire tout seul en suivant les explications et instructions données ici... et du coup si tu n'arrive pas à  ça, c'est certainement qu'il y a des bases à  revoir avant de s'attaquer à  ce genre de truc ? Par exemple si tu n'arrives pas tout seul à  coder les méthodes genre numberOfSections à  partir de là , abandonne ce projet pour l'instant car si tu bloques là  tu vas bloquer à  plein d'autres endroits, et repars de bases plus simples sur des projets et programmes d'exemples plus simples.
  • wallfredwallfred Membre
    04:22 modifié #17
    Merci beaucoup pour le code..
    Oui il faut que j'apprenne mieux les bases.
Connectez-vous ou Inscrivez-vous pour répondre.