List half of an array

Dear all, I have initialized an array of a variable number of items

 @State private var squadre: [Squadre] = Squadre.test()

Now, I'm showing in the following way the array:

List {
           ForEach(squadre) { squadre in
                HStack {
                       Text(squadre.squadra)
                       Text("\(squadre.punti)")
                              }
                    }
        }

I'd like to perform the following actions:

  • Sort the array by the value "punti" (Int type)
  • Show only the first half of the array in the list

Do you have clue of how I can achieve it? I tried indexing the array, but didn't make it.

Please support me.

Thanks, A.

Answered by Claude31 in 782719022

Here it is. It is a little bit verbose to make it easier to understand.

Don't forget to close the thread if that works. Otherwise, explain what is the problem. And do the same on your older threads to keep forum clean.

struct Squadre: Identifiable, Equatable {  // ad hoc, for testing. Adapt to yours.
    var id = UUID()
    var squadra: String
    var punti: Int
    
    static func test() -> [Squadre] {
        [Squadre(squadra: "One", punti: 1), Squadre(squadra: "Seven", punti: 7), Squadre(squadra: "Three", punti: 3), Squadre(squadra: "Nine", punti: 9), Squadre(squadra: "Twelve", punti: 12), Squadre(squadra: "Five", punti: 5), Squadre(squadra: "Four", punti: 4)]
    }
}

struct ContentView: View {
    @State private var squadre: [Squadre] = Squadre.test()
    @State private var firstHalfSquadre: [Squadre] = []
    @State private var secondHalfSquadre: [Squadre] = []

    var body: some View {
        HStack {
            List {
                ForEach($firstHalfSquadre, id: \.id) { $sq in  // do not reuse squadre as name
                    HStack {
                        Text(sq.squadra)
                        Text("\(sq.punti)")
                    }
                }
            }
            List {
                ForEach($secondHalfSquadre, id: \.id) { $sq in  // do not reuse squadre as name
                    HStack {
                        Text(sq.squadra)
                        Text("\(sq.punti)")
                    }
                }
            }
        }

        .onAppear {
            let lastHalfCount = squadre.count / 2
            let firstHalfCount = squadre.count - lastHalfCount  // to take care if count is odd

            firstHalfSquadre = squadre.sorted(by: { $0.punti > $1.punti } )  // decreasing order
            firstHalfSquadre = firstHalfSquadre.dropLast(lastHalfCount)     // remove last half

            secondHalfSquadre = squadre.sorted(by: { $0.punti > $1.punti } )  // decreasing order
            secondHalfSquadre = Array(secondHalfSquadre.dropFirst(firstHalfCount))  // keep last half
                     // dropFirst creates a subsequence, not an array… So need to convert to Array.
       }

        .onChange(of: squadre) {
            let lastHalfCount = squadre.count / 2
            let firstHalfCount = squadre.count - lastHalfCount
            firstHalfSquadre = squadre.sorted(by: { $0.punti > $1.punti } ).dropLast(lastHalfCount)
            secondHalfSquadre = Array(squadre.sorted(by: { $0.punti > $1.punti } ).dropFirst(firstHalfCount)) 
        }
        
        // To play with it…
        
        Button(action: {
            squadre.append(Squadre(squadra: "Two", punti: 2))
        }) {
            Text("Add Punti 2")
        }

        Button(action: {
            squadre[0] = Squadre(squadra: "New One", punti: 10)
        }) {
            Text("Modify first \(squadre[0].squadra) to 10")
        }

        Button(action: {
            let newValue = squadre[0].punti + 5
            squadre[0] = Squadre(squadra: "New first", punti: newValue)
        }) {
            Text("Add 5 to first")
        }
    }
}

You could build a new array

 @State private var halfSquadre: [Squadre] = []

Then at some point (for instance .onAppear), initialise it

halfSquadre = squadre.sorted(by: { $0.punti < $1.punti } )  // increasing order
let halfCount = squadre.count / 2  // take care if count is uneven : if 11, do you want to display 5 or 6 ?
halfSquadre = halfSquadre.dropLast(halfCount)  // remove last half

Use it in ForEach:

List {
   ForEach(halfSquadre) { squadre in
      HStack {
          Text(squadre.squadra)
           Text("\(squadre.punti)")
       }
   }
}

If you want to update dynamically, use onChange. Note that I created a Squadre struct for the test.

struct Squadre: Identifiable, Equatable {
    var id = UUID()
    var squadra: String
    var punti: Int
    
    static func test() -> [Squadre] {
        [Squadre(squadra: "One", punti: 1), Squadre(squadra: "Seven", punti: 7), Squadre(squadra: "Three", punti: 3), Squadre(squadra: "Nine", punti: 9), Squadre(squadra: "Twelve", punti: 12), Squadre(squadra: "Five", punti: 5), Squadre(squadra: "Four", punti: 4)]
    }
}

struct ContentView: View {
    @State private var squadre: [Squadre] = Squadre.test()
    @State private var halfSquadre: [Squadre] = []

    var body: some View {
        VStack {
            List {
                ForEach($halfSquadre, id: \.id) { $sq in  // do not reuse squadre as name
                    HStack {
                        Text(sq.squadra)
                        Text("\(sq.punti)")
                    }
                }
            }
            .onAppear {
                halfSquadre = squadre.sorted(by: { $0.punti < $1.punti } )  // increasing order
                let halfCount = squadre.count / 2  // take care if count is uneven : if 11, do you want to display 5 or 6 ?
                halfSquadre = halfSquadre.dropLast(halfCount)  // remove last half
            }
            .onChange(of: squadre) {
                halfSquadre = squadre.sorted(by: { $0.punti < $1.punti } ).dropLast(squadre.count / 2)
            }
            
            Button(action: {
                squadre.append(Squadre(squadra: "Two", punti: 2))
            }) {
                Text("Add Punti 2")
            }

            Button(action: {
                squadre[0] = Squadre(squadra: "Ten", punti: 10)
            }) {
                Text("Modify first to 10")
            }
        }
    }
}

Thanks a lot Claude31 - I'll try.

Sorry I have not been so precise in writing my question. My idea is:

  • to have a decreasing order (from the max of punti to the min)
  • show the first half in one List, the second half in a second List

Any changes to your kindly suggested code?

Thanks, A.

Accepted Answer

Here it is. It is a little bit verbose to make it easier to understand.

Don't forget to close the thread if that works. Otherwise, explain what is the problem. And do the same on your older threads to keep forum clean.

struct Squadre: Identifiable, Equatable {  // ad hoc, for testing. Adapt to yours.
    var id = UUID()
    var squadra: String
    var punti: Int
    
    static func test() -> [Squadre] {
        [Squadre(squadra: "One", punti: 1), Squadre(squadra: "Seven", punti: 7), Squadre(squadra: "Three", punti: 3), Squadre(squadra: "Nine", punti: 9), Squadre(squadra: "Twelve", punti: 12), Squadre(squadra: "Five", punti: 5), Squadre(squadra: "Four", punti: 4)]
    }
}

struct ContentView: View {
    @State private var squadre: [Squadre] = Squadre.test()
    @State private var firstHalfSquadre: [Squadre] = []
    @State private var secondHalfSquadre: [Squadre] = []

    var body: some View {
        HStack {
            List {
                ForEach($firstHalfSquadre, id: \.id) { $sq in  // do not reuse squadre as name
                    HStack {
                        Text(sq.squadra)
                        Text("\(sq.punti)")
                    }
                }
            }
            List {
                ForEach($secondHalfSquadre, id: \.id) { $sq in  // do not reuse squadre as name
                    HStack {
                        Text(sq.squadra)
                        Text("\(sq.punti)")
                    }
                }
            }
        }

        .onAppear {
            let lastHalfCount = squadre.count / 2
            let firstHalfCount = squadre.count - lastHalfCount  // to take care if count is odd

            firstHalfSquadre = squadre.sorted(by: { $0.punti > $1.punti } )  // decreasing order
            firstHalfSquadre = firstHalfSquadre.dropLast(lastHalfCount)     // remove last half

            secondHalfSquadre = squadre.sorted(by: { $0.punti > $1.punti } )  // decreasing order
            secondHalfSquadre = Array(secondHalfSquadre.dropFirst(firstHalfCount))  // keep last half
                     // dropFirst creates a subsequence, not an array… So need to convert to Array.
       }

        .onChange(of: squadre) {
            let lastHalfCount = squadre.count / 2
            let firstHalfCount = squadre.count - lastHalfCount
            firstHalfSquadre = squadre.sorted(by: { $0.punti > $1.punti } ).dropLast(lastHalfCount)
            secondHalfSquadre = Array(squadre.sorted(by: { $0.punti > $1.punti } ).dropFirst(firstHalfCount)) 
        }
        
        // To play with it…
        
        Button(action: {
            squadre.append(Squadre(squadra: "Two", punti: 2))
        }) {
            Text("Add Punti 2")
        }

        Button(action: {
            squadre[0] = Squadre(squadra: "New One", punti: 10)
        }) {
            Text("Modify first \(squadre[0].squadra) to 10")
        }

        Button(action: {
            let newValue = squadre[0].punti + 5
            squadre[0] = Squadre(squadra: "New first", punti: newValue)
        }) {
            Text("Add 5 to first")
        }
    }
}

Thanks Calude31, that works!

I should have also closed this thread and the others - please let me know if any other action is required on my side.

Thanks, A.

List half of an array
 
 
Q