TableView Sections avec des dates?

Bonsoir tout le monde :slight_smile:

J’essaye d’ajouter des sections à ma tableView.
Pour ajouter des sections “simple”, pas de soucis, ça fonctionne, mais maintenant, j’aimerai ajouter des sections qui sont en fait des dates d’objets… Sur ce point, je bloque.

Dans mon code, je récupère tous les éléments à afficher dans ma tableView via un manager de ces éléments:

let elements = elementsManager.getAll()

elementsManager.getAll() me permet de récupérer tous les éléments (stockés avec Realm) que je souhaite afficher.

Ces éléments sont composés d’un nom et d’une date:

class Element:Object {
    @objc dynamic private var nom:String = ""
    @objc dynamic var date:Date = Date()
}

La récupération de tous ces éléments fonctionne bien.

Cependant, j’aimerai maintenant afficher ces éléments dans ma tableView en les séparant dans différentes sections en fonction de leur date.

Par exemple:
Si j’ai 3 éléments:

let elt1 = Element()
elt1.nom = "Element 1"
elt1.date = "25-12-17" // J'utilise Date() pour définir la date, donc je mets un texte ici à titre d'exemple

let elt2 = Element()
elt1.nom = "Element 2"
elt1.date = "26-12-17" // J'utilise Date() pour définir la date, donc je mets un texte ici à titre d'exemple

let elt3 = Element()
elt1.nom = "Element 3"
elt1.date = "26-12-17" // J'utilise Date() pour définir la date, donc je mets un texte ici à titre d'exemple

Avec ces objets, dans ma tableView, j’aimerai que cela s’affiche de cette manière:

25-12-17
    Element 1
26-12-17
    Element 2
    Element 3

Comment puis-je faire?
Je suppose que si je récupère tous les éléments avec Realm, puis que je les parcourt un à un pour récupérer leurs dates pour en faire un tableau (pour les sections), ça devrait fonctionner, mais c’est pas génial si?

Vos aides sont les bienvenues! :slight_smile:

Bonne soirée, et joyeuses fêtes de fin d’année à tout le monde :slight_smile:

Alexandre

Hello Alexandre,

Si tu as un tableau avec tous tes éléments du quelque chose de ce style : let array = ["25-12-17" : ["Element 1"], "26-12-17" : ["Element 2", "Element 3"], etc.]

Tu peux retourner le nombre de sections en faisant :

func numberOfSections(in tableView: UITableView) -> Int {
    return array.keys.count
}

Tu auras donc une section pour chaque clé d’entrée de ton tableau de valeurs. Et pour avoir le nombre de lignes de ta tableView, tu peux faire quelque chose du genre :

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let sectionArray = array[array.keys[section]]
    return sectionArray.count
}

Et tu devrais avoir une tableView avec le nombre de lignes qu’il te faut pour afficher tes données ainsi qu’une séparation en section.

Hello @schtipoun
Oui, dans ce cas là, ça fonctionne bien, mais je n’ai pas un tableau sous cette forma là, j’a plutôt un Results avec mes objets Realm, donc je me demandais si je ne savais pas faire autrement que de retravailler le tableau ?
Merci,

Arf, comme je ne connais vraiment pas Realm, je ne peux jamais vraiment aider sur ce sujet. Peut-être qu’un jour j’y jetterai un coup d’oeil mais ce n’est pas dans mes priorités immédiates :slight_smile:

Pas de soucis, merci pour tes réponses :slight_smile:

regarde comment on filtre avec Realm => https://realm.io/docs/swift/latest/#queries

tu pourrais peut être remplir un tableau en filtrant ta base de donnée Realm

c’est une piste

Les objets Results sont exactement comme des tableaux, inutiles de les convertir, tu peux les utiliser pour remplir ta table view.
Tu peux utiliser les filtres que mentionnait @alexandre.cane pour créer un Results pour chacune de tes sections.
Si tu ne connais pas à l’avance les dates présentes tu peux utiliser ce genre d’astuces : https://stackoverflow.com/questions/30245319/return-unique-distinct-values-with-realm-query

2 « J'aime »

Merci, je vais y jeter un oeil :slight_smile:

Bonjour @mbritto

Désolé pour le petit moment d’absence sur cette partie, mais j’y ai enfin jeté un oeil! :slight_smile:

J’ai pu utiliser ceci:

return Array(Set(realm.objects(Race.self).value(forKey: "mission.label") as! [String]))

qui fonctionne parfaitement.
Cependant, j’aimerai appliquer cela sur des dates, j’ai donc essayé de cette manière:

return Array(Set(realm.objects(Race.self).value(forKey: "date") as! [Date]))

mais je me suis vite rendu compte de quelque chose: comme mes dates sont, par exemple:

2018-02-05 10:32:47 +0000

En fait, il n’y en aura jamais deux les mêmes, puisqu’elles sont à la seconde près…

Est-ce qu’il est possible de caster ces dates avec un format du genre dd/MM/yyyy ?
Ou peut-être un autre moyen ?

Merci pour vos réponses, :slight_smile:

Alexandre

Salut @Alexandre,

Je t’invite à regarder du côté du DateFormatter.
Tu devrais y trouver ton bonheur :wink:

1 « J'aime »

Bonjour @iMrMaximus :slight_smile:

Est-ce que je saurais appliquer un DateFormatter directement sur cette requête ?
J’arrive à appliquer un format de date sur un objet spécifique, mais dans le cas d’une requête ?

return Array(Set(realm.objects(Race.self).value(forKey: "date") as! [Date]))

Merci :slight_smile:

Non, DateFormatter ne formate qu’une date à la fois.
Ici, tu peux, par exemple, mapper ta liste pour récupérer uniquement la liste de date formatée (mais, je pense que tu vas devoir passer par un “reduce” après)

Sinon tu peux ajouter dans ta class Race @objc dynamic var sectionDate: String et tu rentres dans ta DB les dates formatées comme tu veux. Bon c’est vrai que du coup tu as deux fois les mêmes infos dans ta DB mais tu te compliques moins la vie pour les Results. Je ne sais pas ce qu’en pense les autres qui ont plus d’experience que moi sur le sujet.

Bonsoir @alexandre.cane et @iMrMaximus :slight_smile:

@alexandre.cane Effectivement, je pourrais faire comme ça, mais du coup, comme tu l’as dit, il y aura duplication de données, ce que j’aimerai éviter.

@iMrMaximus On m’avait effectivement conseillé le “reduce”, mais je n’ai pas réussis à l’utiliser…
Voici à quoi j’arrivais au mieux:

let calendar = Calendar.current
let distinctRacesDates = races.reduce(into: [:], { (counts, race) in
    counts[calendar.dateComponents([.day, .month, .year], from: race.getDate()), default: 0] += 1
})
print("Distinct Races: \(distinctRacesDates)")     

qui me donnait:

Distinct Races: [year: 2018 month: 2 day: 1 isLeapMonth: false : 2]

Malgré le fait que le reduce avait l’air de fonctionner (puisqu’il m’indiquait bien qu’il y avait 2 occurrences), je n’arrivais pas à tout remettre sous forme de liste…

Si vous avez une idée, je suis preneur! :slight_smile:

Merci à vous,
Bonne soirée,

Alexandre

@Alexandre pour info, dans la plupart de mes projets j’ai l’habitude de créer un class ToolBox, c’est un peu ma class où je place des fonctions divers et variées. Par exemple caster une date en String. Voila quelques fonctions en rapport avec les dates :

// Cast de la date en "String"
extension Date {
    func toString( dateFormat format  : String ) -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = format
        dateFormatter.locale = Locale.init(identifier: "fr_FR")
        return dateFormatter.string(from: self)
    }
}

class Toolbox {

    private let _today = Date()
    
    // --------------- Affichage de la date 01/01/2017 ------------
    func getDateDay() -> String {
        let _todayToString = _today.toString(dateFormat: "dd/MM/YYYY")
        return _todayToString
    }
    
    // --------------- Affichage de la date 01-01-2017 ------------
    func getDateDayForProxy() -> String {
        let _todayToString = _today.toString(dateFormat: "dd-MM-YYYY")
        return _todayToString
    }
    
    // --------------- Affichage de la date '2017-01-01 HH:mm:ss' ------------
    func getDateDayForPost() -> String {
        let _todayToString = _today.toString(dateFormat: "YYYY-MM-dd HH:mm:ss")
        return _todayToString
    }
}

@alexandre.cane Ah, je vois qu’on a les mêmes habitudes, de créer des petites librairies que l’on réutilise de projet en projet pour nous faciliter la vie :grin:

Le soucis ici, par contre, ce n’est pas vraiment de convertir une date dans un autre format, mais plutôt d’arriver à le faire avec le code suivant:

return Array(Set(realm.objects(Race.self).value(forKey: "date") as! [Date]))

qui ne me permet pas de convertir mes dates directement…

Sinon, petite question par rapport à tes fonctions: pourquoi ne pas faire comme ceci:

func getDateDay() -> String {
    return _today.toString(dateFormat: "dd/MM/YYYY")
}

plutôt que:

func getDateDay() -> String {
    let _todayToString = _today.toString(dateFormat: "dd/MM/YYYY")
    return _todayToString
}

?

Je continue de cherche une solution au problème que je rencontre par rapport aux sections… :slight_smile:
Même si je commence à penser que le plus simple serait que je me fasse une petite méthode qui me retournerait les dates sans doublons… Mais pas sur que ce soit le plus performant…

Bonne soirée,

J’ai fais un copier coller et effacé certain truc propre au projet dans lequel je l’ai pris. Il y avait d’autre manip entre mais tu as raison sur le principe ci-dessus

Bonjour @Alexandre,

Effectivement, je t’invitais à utiliser DateFormatter comme te le conseillait @alexandre.cane (au passage, merci pour le conseil @alexandre.cane).

Au fait, dis-moi, pourquoi fais-tu ça ?
Array(Set(realm.objects(Race.self).value(forKey: "date") as! [Date]))

Bon, sinon, après avoir ta liste de date, tu peux faire un reduce pour supprimer la dates doublons, puis, tu tris ta liste de dates (une liste de dates triées, c’est toujours mieux :blush:) et ensuite, tu “map” le tout en “string” au format “dd/MM/YYYY” pour avoir une liste correspondant aux sections…
Est-ce que ça corrige ton 1er soucis ?

Ou sinon, tu pourrais faire une liste de sous-liste où chaque sous-liste est une liste d’objet à la même date… (par contre, dans ce cas là, inutile de définir une liste de date String au format “dd/MM/YYYY”, parce que tu peux récupérer ces dates aurement)

Hello @iMrMaximus

Je fais ça pour tenter de récupérer toutes mes dates de manière unique (cela fonctionne quand les informations sont exactement les mêmes (comme un nom par exemple), mais avec les dates, cela ne fonctionne pas, puisque les dates comportent également les secondes, donc les dates ne sont jamais les même)

Array(Set(realm.objects(Race.self).value(forKey: "date") as! [Date]))`

Pour ta première idée avec le reduce, je suis partant d’essayer, mais je ne comprends pas comment utiliser le reduce… Un exemple et des explications seraient les bienvenues :slight_smile:

Sinon, pour ta deuxième idée, je pense qu’elle peut fonctionner aussi, mais quelle est la mieux au niveau performance ?

Il faudrait que je regarde aussi, si avec Realm, si je fais un filtre du genre “ddMMyyyy” sur une date du genre “ddMMyyyy hh:mm:ss” cela fonctionne ou si cela pose problème…

Merci pour le temps que vous prenez pour me répondre :slight_smile:

Ok, mais je me demandais, est-ce que :
Array(Set(realm.objects(Race.self).value(forKey: "date") as! [Date]))
ne serait pas équivalent à :
realm.objects(Race.self).value(forKey: "date") as! [Date]
?

Pour un exemple de “reduce”, et voici :slight_smile: avec une liste de dates :wink: :

let dates = ["09/02/2018", "08/02/2018", "06/02/2018", "06/02/2018", "07/02/2018", "06/02/2018", "08/02/2018"]
dates.reduce(into: [String]()) { (res, dateS) in
    if res.index(of: dateS) == nil {
        res.append(dateS)
    }
}