【问题标题】:Filter an (Codable) array by another array通过另一个数组过滤(可编码)数组
【发布时间】:2020-12-18 10:06:26
【问题描述】:

我正在尝试按 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 { $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]

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

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

然后根据此 SO https://stackoverflow.com/a/37357869/14414215 对其进行了简化并删除了强制展开,变成了这一行

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

【问题讨论】:

    标签: swift codable


    【解决方案1】:

    您需要对收藏夹数组中的每个 id 进行过滤。结果,您得到一个数组数组。要获得最终数组,您需要将这些数组连接到单个数组。 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
    

    【讨论】:

    • 不知何故它不允许我执行它。cannot convert value of type [Any] to expected argument type 'Int' 我试图将其转换为 Int $0.id == favoriteID as? Int 但没有奏效。为什么 favoriteID 的类型是 [Any]?
    • @myjunk I can't reproduce that error。请出示minimal reproducible example。要么你错误地应用了我的答案,要么你没有在你的问题中展示完整的图片。
    • 你说得对。我认为这与我存储数组的方式有关。 (我将它存储到 UserDefault 中,它被存储为 [Any]。我尝试将其转换为 as? [Int] 但不工作。我将更新代码。感谢您的帮助。
    • @myjunk 您是否将favouriteID 转换为[Any]?你应该改用workoutFav。看起来过滤收藏夹问题解决了不是吗?如果投射workoutFav 仍然不起作用,那就是different question,请考虑单独发布。记得添加minimal reproducible example :)
    • 这样做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 那么这是一个差异问题吗?
    【解决方案2】:

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

     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
        })
    

    【讨论】:

    • 锻炼列表作为 JSON 文件存储在应用程序中,我正在考虑将最喜欢的列表存储在 UserDefaults 中(因为它的 ID 数量很少)
    • 谢谢。我还没有学习如何使用 CoreData,但这将是下一步。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 2020-11-25
    • 1970-01-01
    • 2015-11-29
    • 2019-05-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多