Hello everyone and happy new year,
I had learned on this site to create dictionaries to prepare sections of lists from a property of an object, for example:
struct Lieu: Identifiable, Hashable {
var id = UUID().uuidString
var nom: String
var dateAjout = Date()
var img: String
var tag: String
var tag2: String
var tag3: String
}
Preparation of the dictionary with "tag" as key:
private var lieuxParTag: [String: [Lieu]] {
Dictionary(grouping: listeDeLieux) { lieu in
lieu.tag
}
}
private var lieuxTags: [String] {
lieuxParTag.keys.sorted(by: <)
}
Then in the view, it was quite simple:
ForEach (lieuxTags, id: \ .self) {tag in
Section (header: Text (tag)) {
ScrollView (.horizontal, showsIndicators: true) {
LazyHStack {
ForEach (lieuxParTag [tag]!) {Place in
Text (place.name)
}
}
}
}
But how to make sections if "Lieu" contains tag property like this:
var tags: [String]
And in this table I integrate all the tags of a "Lieu" for example :
Lieu(nom: Paris, tags: ["tour eiffel", "bouchons"])
Thanks for your help.
CodePudding user response:
There is no ready-made solution for that in the standard library, but you can easily implement that yourself. You loop over your objects and for each object you loop over its tags and add the current object to the dictionary for the current tag:
var dictionary: [String: [Place]] = [:]
for place in places {
for tag in place.tags {
dictionary[tag, default: []].append(place)
}
}
CodePudding user response:
Here is a working example with comments. I also tightened your code up a bit.
struct SectionsFromArrayView: View {
@State private var listeDeLieux: [Lieu] = Lieu.previewData
private var lieuxParTag: [String: [Lieu]] {
// This first loop extracts the different tags from the array into a set
// to unique them so we only iterate each tag once in the next step.
var tagSet: Set<String> = []
for lieu in listeDeLieux {
for tag in lieu.tags {
tagSet.insert(tag)
}
}
// This loop puts together the dictionary by filtering listeDeLieux by tag
var parTag: [String: [Lieu]] = [:]
for tag in Array(tagSet).sorted() {
let lieuArray = listeDeLieux.filter( { $0.tags.contains(tag) })
parTag[tag] = lieuArray
}
return parTag
}
var body: some View {
List {
// Instead of having a separate computed variable lieuxTags, you can sort the
// array of the lieuxParTag.keys
ForEach(lieuxParTag.keys.sorted(by: <), id: \ .self) {tag in
Section(header: Text (tag)) {
ScrollView(.horizontal, showsIndicators: true) {
LazyHStack {
ForEach(lieuxParTag[tag]!) { lieu in
Text (lieu.nom)
}
}
}
}
}
}
}
}
struct Lieu: Identifiable, Hashable {
var id = UUID().uuidString
var nom: String
var dateAjout = Date()
var img: String
// By making tags a set, they are uniqued
var tags: Set<String>
static var previewData: [Lieu] = [
Lieu(nom: "Alfred", img: "pencil", tags: ["pencil", "doc"]),
Lieu(nom: "Ben", img: "pencil", tags: ["pencil"]),
Lieu(nom: "Charles", img: "paperplane", tags: ["paperplane", "doc"]),
Lieu(nom: "Alfred", img: "paperplane", tags: ["paperplane"]),
Lieu(nom: "Alfred", img: "pencil", tags: ["pencil", "doc"])
]
}
If you can make your tags an enum, you can condense the code even further as you already have your limited set. This shrinks lieuxParTag further:
struct SectionsFromArrayView: View {
@State private var listeDeLieux: [Lieu] = Lieu.previewData
private var lieuxParTag: [Tag: [Lieu]] {
var parTag: [Tag: [Lieu]] = [:]
// In order to get an array, Tag.allCases, Tag must conform to CaseIterable
for tag in Tag.allCases {
let lieuArray = listeDeLieux.filter( { $0.tags.contains(tag) })
parTag[tag] = lieuArray
}
return parTag
}
var body: some View {
List {
// In order to sort the keys, Tag must conform to Comparable
ForEach (lieuxParTag.keys.sorted(), id: \ .self) {tag in
Section(header: Text(tag.rawValue)) {
ScrollView(.horizontal, showsIndicators: true) {
LazyHStack {
ForEach(lieuxParTag[tag]!) { lieu in
Text(lieu.nom) Text(Image(systemName: lieu.img))
}
}
}
}
}
}
}
}
struct Lieu: Identifiable, Hashable {
var id = UUID().uuidString
var nom: String
var dateAjout = Date()
var img: String
// By making tags a set, they are uniqued
var tags: Set<Tag>
static var previewData: [Lieu] = [
Lieu(nom: "Alfred", img: Tag.pencil.rawValue, tags: [.pencil, .doc]),
Lieu(nom: "Ben", img: Tag.pencil.rawValue, tags: [.pencil]),
Lieu(nom: "Charles", img: Tag.paperplane.rawValue, tags: [.paperplane, .doc]),
Lieu(nom: "Alfred", img: Tag.paperplane.rawValue, tags: [.paperplane]),
Lieu(nom: "Alfred", img: Tag.pencil.rawValue, tags: [.pencil, .doc])
]
}
enum Tag: String, CaseIterable, Comparable {
case doc, paperplane, pencil
static func < (lhs: Tag, rhs: Tag) -> Bool {
lhs.rawValue < rhs.rawValue
}
}
This was a well asked first question. The only thing I would recommend is always supplying a Minimal, Reproducible Example. You want to make it as easy as possible for someone to help.