-1

I'm trying to filter my json data by IDs (trying mark some favourites and filter using it)

struct workoutList : Codable {
  let id : Int
  let title : String
  let tag : String
}


func selectedWorkoutGroup(libraryFilter: Int, jsonErgWorkouts:[workoutList], workoutGroupBox: UITextField) -> [workoutList] {
  var selectedGroup = [workoutList]()
  let workoutFav = [1,10,100]

  if libraryFilter == 0 {
    // This works because I'm filtering based on 1 specific item
    selectedGroup = jsonErgWorkouts.filter { $0.tag == workoutGroupBox.text } 
  } else if libraryFilter == 1 {
    // Here I want to filter and show only the favorites
    selectedGroup = jsonErgWorkouts.filter { $0.id } // 
    print("selectedGroup:\(selectedGroup)")
  }
  return selectedGroup
}

in the above code, the filter works when I have 1(one) something specific item to filter and then I get the entire json array with that tag.

Now I want to implement a favorite list, where the user selects for example ID == [1, 10 ,100] as their favourite.

How can I use the filter command to do it? I tried a few things and searched through SO (but doesn't work). Most of the answers are based on filtering based on specific items eg:

selectedGroup = jsonErgWorkouts.filter { workoutFav?.contains($0.id) }

edit: (omitted that I am using/storing the favourites in userDefaults. This code gives the error of "type of expression is ambiguous without more context"

    func selectedWorkoutGroup(libraryFilter: Int, jsonErgWorkouts:[workoutList], workoutGroupBox: UITextField) -> [workoutList] {
      var selectedGroup = [workoutList]()
      UserDefaults.standard.set([1,10,100], forKey: "workoutFavorite")
      
      /// This one gets stored as [Any] so I cast it to [Int]
      let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as? [Int]

     if libraryFilter == 0 {
        // This works because I'm filtering based on 1 specific item
        selectedGroup = jsonErgWorkouts.filter { $0.tag == workoutGroupBox.text } 
      } else if libraryFilter == 1 {
         selectedGroup = workoutFav.flatMap { favouriteId in // for each favourite ID
         jsonErgWorkouts.filter { $0.id == favouriteId } // This returns Error "type of expression is ambiguous without more context"
         } // flatMap joins all those arrays returns by "filter" together, no need to do anything else

        print("selectedGroup:\(selectedGroup)")
      }
      return selectedGroup
    }

Final Solution: Changing from This

let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as? [Int]

to This (notice the as! instead of as?)

let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as! [Int]

works using @sweeper's answer. Thanks

Update: Figured out why this error occurred "type of expression is ambiguous without more context" when casting the output of UserDefaults as? [Int] and had to use as! [Int]

But using as! [Int] force unwrapping it causes app to crash if the user did not have any favorites saved into the UserDefault. (Which I then had to code around) like below

var workoutFav = [Int]()
  
if !(UserDefaults.standard.array(forKey: "workoutFavorite") == nil) {
   workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as! [Int]
}

Which was then simplified and removed the force unwrapping based on this SO https://stackoverflow.com/a/37357869/14414215 to become this one-line

  let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as? [Int] ?? [Int]()

2 Answers 2

1

You need to do that filter for each id in the favourites array. You get an array of arrays as a result. To get the final array, you need to join those arrays to a single array. This "map each thing to an array and join the arrays" operation is what a flatMap does:

workoutFav.flatMap { favouriteId in // for each favourite ID
    jsonErgWorkouts.filter { $0.id == favouriteId } // find workouts that match the ID
} // flatMap joins all those arrays returns by "filter" together, no need to do anything else
Sign up to request clarification or add additional context in comments.

6 Comments

Somehow it's not allowing me to execute it.cannot convert value of type [Any] to expected argument type 'Int' I tried to cast it as Int $0.id == favoriteID as? Int but didn't work. Why is it that favoriteID is of type [Any]?
@myjunk I can't reproduce that error. Please show a minimal reproducible example. Either you incorrectly applied my answer, or you haven't showed the full picture in your question.
you're right.. I think it's has to do with the way I store my Array. (I'm storing it into UserDefault and it's being stored as [Any]. I tried to cast it to as? [Int] but not working.. I'll update the code.. Thanks for help.
@myjunk Are you casting favouriteID as [Any]? You should cast workoutFav instead. Looks like the filtering favourites problem is solved isn't it? If casting workoutFav still doesn't work, that would be a different question, consider posting it separately. Remember to include a minimal reproducible example :)
did this let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as? [Int] then your solution selectedGroup = workoutFav.flatMap { favouriteId in jsonErgWorkouts.filter { $0.id == favouriteId } } gives type of expression is ambiguous without more context is this a diff issue then?
|
1

First thing first please give a struct name with a capital so you can distinguish between instance of it. Second you need to have new array where you will store each favorite, and store permanently that array, core data or some base on server, form there you will fetch favorites. The better way is to add property like isFavorite: Bool that is false by default, and if user change it you can set it to be true, in that way you can avoid using ids for that and you can store whole workout's in one array to core data or base that you use, after that you can fetch from there with

 let favorites = workouts.compactMap { $0.isFavorite == true }

Here you go in that way, but just to mention it highly recommended that you store those type of data outside User defaults.

struct Fav {
        let name: String
        let id: String
    }
    
    let df = UserDefaults.standard
    let jk = ["aaa", "bbb", "cccc"]
    df.setValue(jk, forKey: "favorites")
    
    let fav1 = Fav(name: "zzz", id: "aaa")
    let fav2 = Fav(name: "bbb", id: "qqq")
    let favs = [fav1, fav2]
    
    let favIDs = df.value(forKey: "favorites") as? [String]
    
    favIDs?.forEach({ (id) in
        let f = favs.filter({$0.id == id}) // here it is
    })

2 Comments

The list of workouts are stored as a JSON file within the app and I'm thinking of storing the favorite list within UserDefaults (since it's gonna be small qty of IDs)
Thanks. I have yet to learn how to use CoreData but that would be the next step.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.