Home > Blockchain >  How to create sections in a list from an array of a property?
How to create sections in a list from an array of a property?

Time:01-02

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.

  • Related