用另一个数组过滤一个 (Codable) 数组

Filter an (Codable) array by another array

我正在尝试按 ID 过滤我的 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 { [=10=].tag == workoutGroupBox.text } 
  } else if libraryFilter == 1 {
    // Here I want to filter and show only the favorites
    selectedGroup = jsonErgWorkouts.filter { [=10=].id } // 
    print("selectedGroup:\(selectedGroup)")
  }
  return selectedGroup
}

在上面的代码中,当我有 1(一个)特定项目要过滤然后我得到带有该标签的整个 json 数组时过滤器工作。

现在我想实现一个收藏夹列表,例如用户选择 ID == [1, 10 ,100] 作为他们的收藏夹。

如何使用过滤命令来完成?我尝试了一些东西并通过 SO 进行了搜索(但没有用)。大多数答案都是基于基于特定项目的过滤,例如:

selectedGroup = jsonErgWorkouts.filter { workoutFav?.contains([=11=].id) }

编辑:(省略了我是 using/storing 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 { [=12=].tag == workoutGroupBox.text } 
      } else if libraryFilter == 1 {
         selectedGroup = workoutFav.flatMap { favouriteId in // for each favourite ID
         jsonErgWorkouts.filter { [=12=].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]

对此(注意 as! 而不是 as?)

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

使用@sweeper 的回答有效。谢谢

更新: 弄清楚为什么在转换 UserDefaults as? [Int] 的输出时会出现此错误“表达式类型不明确,没有更多上下文”并且必须使用 as! [Int]

但是如果用户没有将任何收藏夹保存到 UserDefault 中,则使用 as! [Int] 强制解包会导致应用程序崩溃。 (然后我不得不编写代码)如下所示

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

然后根据这个 SO 简化并删除了力展开成为这个 one-line

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

您需要为收藏夹数组中的每个 ID 执行该过滤器。结果你得到一个数组数组。要获得最终数组,您需要将这些数组连接到一个数组中。这种“将每个事物映射到一个数组并加入数组”操作就是 flatMap 所做的:

workoutFav.flatMap { favouriteId in // for each favourite ID
    jsonErgWorkouts.filter { [=10=].id == favouriteId } // find workouts that match the ID
} // flatMap joins all those arrays returns by "filter" together, no need to do anything else

首先请给结构名称一个大写字母,这样你就可以区分它的实例。其次,您需要有一个新数组,您将在其中存储每个收藏夹,并永久存储该数组、核心数据或服务器上的某些基础,从那里您将获取收藏夹。 更好的方法是添加 属性 like isFavorite: Bool 默认情况下为 false,如果用户更改它,您可以将其设置为 true,这样您就可以避免为此使用 id,并且可以存储整个锻炼在一个数组中到您使用的核心数据或基础,之后您可以使用

从那里获取
 let favorites = workouts.compactMap { [=10=].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({[=11=].id == id}) // here it is
    })