Swift:在数组中组合2个或更多自定义对象

下面是我的自定义对象类。

class UserGroups: NSObject { let groupName: String let users: [CheckIn]? init(json:JSON) { self.groupName = json[Constants.Models.UserGroups.groupName].stringValue self.users = UserGroups.getUserGroupsList(jsonArray: json[Constants.Models.UserGroups.users].arrayValue) } class func getUserGroupsList(jsonArray: [JSON]) -> [CheckIn]{ return jsonArray.flatMap({ (jsonItem: JSON) -> CheckIn in return CheckIn(json: jsonItem) }) } } 

我有一系列上面的自定义对象。 如何通过合并具有相同groupName的每个对象的用户 ,将2个或更多自定义对象组合到单个对象中。

下面是我的CheckIn模型:

class CheckIn:NSObject {

 let id: String let firstName: String let lastName: String let latitude: String let longitude: String let hint: String init(json: JSON) { self.id = json[Constants.Models.CheckIn.id].stringValue self.firstName = json[Constants.Models.CheckIn.firstName].stringValue self.lastName = json[Constants.Models.CheckIn.lastName].stringValue self.hint = json[Constants.Models.CheckIn.hint].stringValue self.latitude = json["location"][Constants.Models.CheckIn.latitude].stringValue self.longitude = json["location"][Constants.Models.CheckIn.longitude].stringValue } 

}

id字段在CheckIn中不是唯一的。

这是一个略微简化的示例,显示如何组合具有相同组名的组。

这是UserGroup类。 users现在是变量( var ),因为我们将向组添加元素以组合它们。

 class UserGroups: NSObject { let groupName: String var users: [String]? init(groupName: String, users: [String]?) { self.groupName = groupName self.users = users } } 

这里有三组,两组共享同一组名, Blues

 let group1 = UserGroups(groupName: "Blues", users: ["Tom", "Huck", "Jim"]) let group2 = UserGroups(groupName: "Reds", users: ["Jo", "Ben", "Tommy"]) let group3 = UserGroups(groupName: "Blues", users: ["Polly", "Watson", "Douglas"]) 

接下来,我们将所有组放在一个数组中。

 let allGroups = [group1, group2, group3] 

在这里,我们使用Swift的reduce函数允许我们将数组减少到只有具有唯一组名的组。

 let compacted = allGroups.reduce([UserGroups](), { partialResult, group in var dupe = partialResult.filter {$0.groupName == group.groupName }.first if let dupeGroup = dupe { dupeGroup.users?.append(contentsOf: group.users ?? []) return partialResult } else { var newPartialResult = partialResult newPartialResult.append(group) return newPartialResult } }) 

数组现在缩减为唯一的组,我们借助Swift的map函数打印出所有组及其用户。

 print(compacted.map { $0.users }) // Prints [ Optional(["Tom", "Huck", "Jim", "Polly", "Watson", "Douglas"]), Optional(["Jo", "Ben", "Tommy"]) ] 

解决方案

您没有包含CheckIn模型,但我会假设它具有某种对每个用户唯一的id字段。 我们将使用它来使对象Hashable

 // Add this to your file outside of the UserGroups class extension CheckIn: Hashable { var hashValue: Int { return self.id } } 

使其成为Hashable允许您将Array转换为Set ,它不允许重复,并将以非常有效的方式删除它们。

 // Change getUserGroupsList as follows class func getUserGroupsList(jsonArray: [JSON]) -> [CheckIn] { return Array(Set(jsonArray.flatMap({ (jsonItem: JSON) -> CheckIn in return CheckIn(json: jsonItem) }))) } 

可选注意事项

顺便说一下,如果你来自另一种语言,Swift会给你很好的类型推断和闭包参数的默认名称( $0是第一个参数)。 您可以使代码稍微简洁一些,但这是一个首选的品味问题。

 class func getUserGroupsList(jsonArray: [JSON]) -> [CheckIn] { return Array(Set(jsonArray.flatMap { CheckIn(json: $0) })) } 

还要考虑您是否真的希望返回值为数组。 如果您希望列表始终具有唯一用户,则使用Set作为返回类型并将转换放回到这样的Array更高效:

 class func getUserGroupsList(jsonArray: [JSON]) -> Set { return Set(jsonArray.flatMap { CheckIn(json: $0) }) } 

最后,考虑一下您是否确实需要users属性是可选的。 对于序列类型,通常使用空序列来表示缺少值。 根据您的具体情况,这可能会简化您的代码。 最终版本如下所示:

 class UserGroups: NSObject { let groupName: String let users: Set init(json:JSON) { self.groupName = json[Constants.Models.UserGroups.groupName].stringValue self.users = UserGroups.getUserGroupsList(jsonArray: json[Constants.Models.UserGroups.users].arrayValue) } class func getUserGroupsList(jsonArray: [JSON]) -> Set { return Set(jsonArray.flatMap { CheckIn(json: $0) }) } } 

维持秩序

需要注意的是, Set不保持项目的顺序。 如果组的顺序很重要,我们可以使用此解决方案:

 class func getUserGroupsList(jsonArray: [JSON]) -> [CheckIn] { var encountered: Set = [] return jsonArray.flatMap { CheckIn(json: $0) }.filter { encountered.update(with: $0) == nil } } 

在这个版本中,我们仍然使用一个集合,但仅用于维护我们已经遇到的一组项目。 如果集合中的update方法已经在集合中,则返回相同的值,如果第一次插入则返回nil 。 我们使用它来将我们的数组过滤到第一次遇到的项目,同时将它们添加到遇到的项目集中,以便在以后再次遇到它们时将其过滤掉。