UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Remove Item from Array

Forums > Swift

Ok, I know this is basic. I am working with Beta 6 of XCode 12 I have the following struct:

struct JSONModel: Codable, Identifiable {
    let id: String
    let instrumentalMP3: String
    let vocalMP3: String
    let hymnName: String
    let hymnNumber: Int
    let sectionIndex: Int
    let alphabetIndex: Int
    let topicIndexes: [Int]
}

I then have a SwiftUI View that has a list. The rows are defined by the following view. When I user taps on the plus button it adds the hymn to the userChosenHymns array and changes the plus button to a minus button. I have it adding to the array but I cant seem to get it to remove that chosen hymn from the array. I'm having trouble getting the index of the where that hymn resides in the array.

Can someone help me with this?

struct CreateAPlaylistSheetRowView: View {
    @State private var changeHymnNameColor = false
    @State private var changeButtonImage = true
    @Binding var userChosenHymns: [JSONModel]
    var hymn: JSONModel

    var body: some View {
        HStack {
            if self.changeButtonImage {
                Button(action: {
                    // MARK: User chooses a hymn
                    self.userChosenHymns.append(self.hymn)
                    self.changeHymnNameColor.toggle()
                    self.changeButtonImage.toggle()
                }) {
                    Image(systemName: "plus.circle")
                        .font(.title)
                        .foregroundColor(Color.themeAccent)
                }
                .padding(.trailing, 10)
            } else {
                Button(action: {
                    // MARK: User chooses a hymn

                    self.changeHymnNameColor.toggle()
                    self.changeButtonImage.toggle()
                }) {
                    Image(systemName: "minus.circle")
                        .font(.title)
                        .foregroundColor(Color.themeAccent)
                }
                .padding(.trailing, 10)
            }

            VStack(alignment: .leading) {
                if self.changeHymnNameColor == false {
                    Text("\(hymn.hymnName)")
                        .lineLimit(1)
                        .font(.headline)
                } else {
                    Text("\(hymn.hymnName)")
                        .lineLimit(1)
                        .font(.headline).foregroundColor(Color.themeAccent)
                }
                Text("Hymn Number: \(hymn.hymnNumber)")
                    .font(.caption)
                    .foregroundColor(Color.themeTertiary)
            }
        }
    }
}

Thanks, Mark

3      

Try using self.userChosenHymns.firstIndex(of:) to find the object's index, and then .remove(at:) to remove it.

3      

Instead of trying to find an index to remove, you could use removeAll(where:) and then you can pass a closure to test for an id matching the selected hymn's id property.

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Ive tried firstIndex of but cant seem to make that work. Can you give me an example.

3      

@roosterboy, can you give me an example because ive tried that as well. i cant seem to get it to work. its either that or i dont know how closures work.

3      

@roosterboy,

I did some research on closures. I'm stuck at this point, this is what I came up with but its not working,

  self.userChosenHymns.removeAll(where: {
    userChosenHymns[$0].id == hymn.id
  })

I get the following error:

Cannot convert value of type 'JSONModel' to expected argument type 'Int'

Not sure what I'm doing wrong. I'm stuck at this point.

Can you help me with this?

Thanks Mark

3      

Ok, @roosterboy. I think I figured it out. I needed to tell the where portion the arguments I needed to pass to the removeAll. This is what I wrote and it seems to be working.

  self.userChosenHymns.removeAll(where: { (jm: JSONModel) -> Bool in
    return jm.id == hymn.id
  })

Am i correct in assuming that removeAll is taking the jm: JSONModel and returning everything else that doesn't match that id. I assume that jm is being passed in when I wrote return jm.id == hymn.id.

3      

Hey @appledad05, wondering if you can help me with a similar issue - I have a similar model setup where I want to allow a user to remove a plant (from a ForEach list of plants) when they click on the settings button. Originally, I had it setup with a list to where users could easily swipe and delete. This was the function that was performed with .onDelete(perform: deleteReminder) for the original list:

func deletePlant(at offsets: IndexSet) {
        if let realm = userDataState.plants.realm {
            try! realm.write {
                realm.delete(userDataState.plants[offsets.first!])
            }
        } else {
            print("hit Else block")
        }
    }

Now, I want it to trigger through an ActionSheet popup, and I'm not sure how to access the specific ID/index of the plant with each card in the ForEach. Any suggestions on how to call the above function outside of the ForEach list?

3      

@appledad05, you could write it like this, using trailing closure syntax and implicit return:

self.userChosenHymns.removeAll {
    $0.id == hymn.id
}

IMO, it looks a little neater and is easier to read.

Am i correct in assuming that removeAll is taking the jm: JSONModel and returning everything else that doesn't match that id.

It's not really returning anything; it's modifying userChosenHymns in place and stripping out anything that passes the boolean test you provide (in this case, matching on id).

3      

Hi @aabeshde ,

Im new to iOS and haven't worked with action sheets.

However, I would assume that if you have some sort of array/collection then when you display the action sheet and press the delete button you can use removeAll on the array and use a trailing closure to filter the array in place. This will remove the plant in question and return everything else back in the same array.

In my case I was building an array of hymns that go with a playlist and if the user chose a hymn it added it to the playlist, but I wanted to give them the ability to unselect the hymn, which in turn would remove it from userChosenHymns array. So doing something like the following worked for me:

self.userChosenHymns.removeAll {
    $0.id == hymn.id
}

id is a unique identifier for each hymn. So you will just need something unique to filter on.

I hope that helped. If I can anwser anything else please let me know.

Appledad05

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.