Отфильтровать (кодируемый) массив другим массивом

Я пытаюсь отфильтровать свои данные json по идентификаторам (пытаясь отметить некоторые избранные и отфильтровать их, используя их)

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
}

в приведенном выше коде фильтр работает, когда у меня есть 1 (один) какой-то конкретный элемент для фильтрации, а затем я получаю весь массив json с этим тегом.

Теперь я хочу реализовать список избранного, в котором пользователь выбирает, например, ID == [1, 10, 100] в качестве своего избранного.

Как я могу использовать команду фильтра, чтобы сделать это? Я пробовал несколько вещей и искал через SO (но не работает). Большинство ответов основаны на фильтрации по конкретным элементам, например:

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

редактировать: (опущено, что я использую/сохраняю избранное в userDefaults. Этот код дает ошибку типа выражения неоднозначно без дополнительного контекста

    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
    }

Окончательное решение: переход от этого

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

to This (обратите внимание на as! вместо as?)

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

работает с использованием ответа @sweeper. Спасибо

Обновление: выяснено, почему возникла эта ошибка. Тип выражения неоднозначен без дополнительного контекста при преобразовании вывода UserDefaults as? [Int] и должен был использовать as! [Int]

Но использование принудительной развертки as! [Int] приводит к сбою приложения, если у пользователя не было избранного, сохраненного в UserDefault. (Что мне затем пришлось кодировать), как показано ниже.

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

Затем это было упрощено и удалено принудительное развертывание на основе этого SO стать этой однострочной

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

person myjunk    schedule 18.12.2020    source источник


Ответы (2)


Вам нужно сделать этот фильтр для каждого идентификатора в массиве избранного. В результате вы получите массив массивов. Чтобы получить окончательный массив, вам нужно объединить эти массивы в один массив. Это сопоставление каждой вещи с массивом и присоединение к операции с массивами - это то, что делает flatMap:

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
person Sweeper    schedule 18.12.2020
comment
Почему-то это не позволяет мне выполнить его. cannot convert value of type [Any] to expected argument type 'Int' Я попытался привести его как Int $0.id == favoriteID as? Int, но не сработало. Почему любимый ID имеет тип [Любой]? - person myjunk; 18.12.2020
comment
@myjunk Я не могу воспроизвести эту ошибку. Покажите минимально воспроизводимый пример. Либо вы неправильно применили мой ответ, либо не показали полной картины в своем вопросе. - person Sweeper; 18.12.2020
comment
вы правы. Я думаю, это связано с тем, как я храню свой массив. (Я сохраняю его в UserDefault, и он сохраняется как [Любой]. Я пытался преобразовать его в as? [Int], но не работает. Я обновлю код. Спасибо за помощь. - person myjunk; 18.12.2020
comment
@myjunk Ты кастуешь favouriteID как [Any]? Вместо этого вы должны разыграть workoutFav. Похоже, проблема с фильтрацией избранного решена, не так ли? Если кастинг workoutFav по-прежнему не работает, это будет другой вопрос, рассмотрите возможность размещения его отдельно. Не забудьте включить минимально воспроизводимый пример :) - person Sweeper; 18.12.2020
comment
сделал это let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as? [Int] тогда ваше решение selectedGroup = workoutFav.flatMap { favouriteId in jsonErgWorkouts.filter { $0.id == favouriteId } } дает type of expression is ambiguous without more context тогда это проблема diff? - person myjunk; 18.12.2020
comment
Спасибо за вашу помощь. Удалось разобраться. Я использовал workoutFav as? [Int], и это дало ошибку. Когда я сделал это как ```workingFav as! [Int]``` тогда он начал работать, используя ваш ответ. Я отмечу это как правильное. Спасибо!! - person myjunk; 18.12.2020

Прежде всего, пожалуйста, дайте имя структуры с заглавной буквы, чтобы вы могли различать ее экземпляры. Во-вторых, вам нужно иметь новый массив, в котором вы будете хранить каждого избранного, и постоянно хранить этот массив, основные данные или некоторую базу на сервере, откуда вы будете получать избранное. Лучший способ - добавить свойство, например isFavorite: Bool, которое по умолчанию является ложным, и если пользователь изменит его, вы можете установить для него значение true, таким образом вы можете избежать использования идентификаторов для этого, и вы можете хранить все тренировки в одном массиве для основные данные или базу, которые вы используете, после чего вы можете получить оттуда с помощью

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

Здесь вы идете таким образом, но просто упомянем, что настоятельно рекомендуется хранить эти типы данных за пределами пользовательских значений по умолчанию.

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
    })
person MMDev11070    schedule 18.12.2020
comment
Список тренировок хранится в виде файла JSON в приложении, и я думаю сохранить список избранного в UserDefaults (поскольку это будет небольшое количество идентификаторов) - person myjunk; 18.12.2020
comment
только что отредактировал пост комментарием - person MMDev11070; 18.12.2020
comment
Спасибо. Мне еще предстоит научиться использовать CoreData, но это будет следующий шаг. - person myjunk; 18.12.2020