在SwiftUI中按值分组Json数据

在SwiftUI中按值分组Json数据,swift,swiftui,Swift,Swiftui,我是SwiftUI的新手,任何帮助都将不胜感激 我有一个Json文件: [ { "id": 1001, "key1": "truck", "key2": "car1", "key3": "motorcycle", }, { "id":

我是SwiftUI的新手,任何帮助都将不胜感激

我有一个Json文件:

[
    {
        "id": 1001,
        "key1": "truck",
        "key2": "car1",
        "key3": "motorcycle",
    },
    {
        "id": 1002,
        "key1": "truck",
        "key2": "car2",
        "key3": "motorcycle",    
    },
    {
        "id": 1003,
        "key1": "truck",
        "key2": "car2",
        "key3": "motorcycle",    
    },
    {
        "id": 1004,
        "key1": "truck",
        "key2": "car2",
        "key3": "motorcycle",    
    },
    {
        "id": 1005,
        "key1": "truck",
        "key2": "car3",
        "key3": "motorcycle",    
    },
]
我可以达到这样的值:

VStack{    
    ForEach(userData.vehicles) { vehicle in
       Text(vehicle.key2)
           .foregroundColor(.white)
    }
}
输出为:

car1
car2
car2
car2
car3
我想看到的是:

car1
car2
car3
有没有办法在SwiftUI中对键进行分组

编辑: @MahdiBM在评论部分要求提供以下代码

车辆申报

final class UserData: ObservableObject {
    @Published var showFavoritesOnly = false
    @Published var vehicles = vehicleInfo
}
读取Json

let vehicleInfo: [Vehicle] = readJSON("vehicleInfo.json")
func readJSON<T: Codable>(_ named: String) -> T {
    if let resPath = Bundle.main.resourcePath {
        do {
            let dirContents = try FileManager.default.contentsOfDirectory(atPath: resPath)
            let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
            let filteredFiles = dirContents.filter{ $0.contains(".json")}
            for fileName in filteredFiles {
                if let documentsURL = documentsURL {
                    let sourceURL = Bundle.main.bundleURL.appendingPathComponent(fileName)
                    let destURL = documentsURL.appendingPathComponent(fileName)
                    do {
                        try FileManager.default.copyItem(at: sourceURL, to: destURL)
                        print("Save to documents")
                    } catch {
                        print("ERROR FileManager.default.copyItem:: ", error)
                    }
                }
            }
        }catch { print("ERRIR 2", error) }
    }
    
    let data: Data
    do {
        let fileURL = try FileManager.default
            .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
            .appendingPathComponent("vehicleInfo.json")
        
        data = try Data(contentsOf: fileURL)
        let foo = try JSONDecoder().decode(T.self, from: data)
//        print(foo)
        
        return foo
    } catch {
        fatalError("Couldn't find  in main bundle.")
    }
}

如是说;“看起来您的帖子大部分是代码;请添加更多详细信息。”


我相信他们需要审查这个要求,因为我没有什么可以说的了。

如果我错了,请纠正我,但据我所知,您有一些
车辆可以重复使用
钥匙2
,您不喜欢这样。相反,您希望
key2
s不重复

var vehicles: [Vehicle] = [...]

// to group all `key2`s together, you can use `.map`:
var allKey2: [String] = vehicles.map { $0.key2 } // or `vehicles.map(\.key2)`, both are the same

// now you have all `key2`s grouped together
// to remove repetitive members, you can store the keys in a `Set` and convert them
// back to `Array`. why `Set`? because `Set` automatically removes repetitive members,
// but it also doesnt have any order for the members so we need to order the members back.
var noRepeatUnorderedAllKey2: [String] = Array(Set(allKey2))
// here we sort based on which member appeared sooner/later in the `allKey2` array
var noRepeatOrderedAllKey2: [String] = noRepeatUnorderedAllKey2.sorted {
    (allKey2.firstIndex(of: $0) ?? 0) < (allKey2.firstIndex(of: $1) ?? 0)
}

如果我错了,请纠正我,但据我所知,您有一些
车辆
可能有重复的
钥匙2
,您不喜欢这样。相反,您希望
key2
s不重复

var vehicles: [Vehicle] = [...]

// to group all `key2`s together, you can use `.map`:
var allKey2: [String] = vehicles.map { $0.key2 } // or `vehicles.map(\.key2)`, both are the same

// now you have all `key2`s grouped together
// to remove repetitive members, you can store the keys in a `Set` and convert them
// back to `Array`. why `Set`? because `Set` automatically removes repetitive members,
// but it also doesnt have any order for the members so we need to order the members back.
var noRepeatUnorderedAllKey2: [String] = Array(Set(allKey2))
// here we sort based on which member appeared sooner/later in the `allKey2` array
var noRepeatOrderedAllKey2: [String] = noRepeatUnorderedAllKey2.sorted {
    (allKey2.firstIndex(of: $0) ?? 0) < (allKey2.firstIndex(of: $1) ?? 0)
}

我将在您的
UserData
类中引入一个新属性,用于保存车辆阵列中的唯一钥匙。设置
车辆
属性时,此属性将自动更新

final class UserData: ObservableObject {
    @Published var showFavoritesOnly = false
    @Published var vehicles: [Vehicle] {
        willSet {
            vehicleKeys = Set(newValue.map(\.key2)).sorted()
        }
    }
    @Published var vehicleKeys: [String] = []
}
然后,您可以在
ForEach
中使用
vehicleKeys
数组,您可以使用一个函数返回所选钥匙的车辆对象

另一种选择是使用字典,因为这样您就可以更容易地访问钥匙对应的车辆对象

    @Published var vehicles: [Vehicle] {
        willSet {
            vehicleKeys = Dictionary(grouping: newValue, by: \.key2)
        }
    }
    @Published var vehicleKeys: [String: [Vehicle]] = [:]

虽然字典没有分类,但我想在您的
UserData
类中引入一个新属性来保存车辆数组中的唯一钥匙。设置
车辆
属性时,此属性将自动更新

final class UserData: ObservableObject {
    @Published var showFavoritesOnly = false
    @Published var vehicles: [Vehicle] {
        willSet {
            vehicleKeys = Set(newValue.map(\.key2)).sorted()
        }
    }
    @Published var vehicleKeys: [String] = []
}
然后,您可以在
ForEach
中使用
vehicleKeys
数组,您可以使用一个函数返回所选钥匙的车辆对象

另一种选择是使用字典,因为这样您就可以更容易地访问钥匙对应的车辆对象

    @Published var vehicles: [Vehicle] {
        willSet {
            vehicleKeys = Dictionary(grouping: newValue, by: \.key2)
        }
    }
    @Published var vehicleKeys: [String: [Vehicle]] = [:]


字典没有分类,但是

车辆声明是什么样子的?如何将json转换为车辆?我将此添加到我的问题中。谢谢,我需要查看车辆才能给您一个适当的答案。类似于
struct Vehicle{var key1:String……}
。尽管上面的代码看起来不错,乍一看并没有任何实际问题。感谢您试图澄清我的问题。我还添加了这个。所以你只想从结构访问key2?如果不是,在JSON中,您是否认为所有元素都是相等的,其中KEY2=“CAR2”,即使ID对于每个都是不同的?车辆声明是什么样子的?如何将json转换为车辆?我将此添加到我的问题中。谢谢,我需要查看车辆才能给您一个适当的答案。类似于
struct Vehicle{var key1:String……}
。尽管上面的代码看起来不错,乍一看并没有任何实际问题。感谢您试图澄清我的问题。我还添加了这个。所以你只想从结构访问key2?如果不是,在JSON中,您是否认为所有元素都是相等的,即使每个ID都不同,KEY2=“CAR2”?谢谢您的时间。我得到的
类“UserData”没有初始值设定项
错误。如何初始化它?
init(){vehicles=[]}
是一种方法。我这样做:
ForEach(userData.vehicleKeys,id:\.self){vehicleKey in Text(vehicleKey)}
没有显示任何数据。你知道可能是什么问题吗?userData是用属性包装器@ObservedObject声明的吗?我使用
@EnvironmentObject var userData:userData
。我还尝试了
@ObservedObject
,但其他一些视图因此崩溃。我相信
@EnvironmentObject
应该可以工作,但是没有机会,没有数据显示。谢谢您抽出时间。我得到的
类“UserData”没有初始值设定项
错误。如何初始化它?
init(){vehicles=[]}
是一种方法。我这样做:
ForEach(userData.vehicleKeys,id:\.self){vehicleKey in Text(vehicleKey)}
没有显示任何数据。你知道可能是什么问题吗?userData是用属性包装器@ObservedObject声明的吗?我使用
@EnvironmentObject var userData:userData
。我还尝试了
@ObservedObject
,但其他一些视图因此崩溃。我相信
@EnvironmentObject
应该可以工作,但是没有机会,没有数据显示。你是对的,非常感谢你抽出时间。我遇到以下错误:
无法在属性初始值设定项中使用实例成员“vehicles”;属性初始值设定项在“self”可用之前运行
。我是这样初始化的:
init(){vehicles=[]}
可能有什么问题?我需要更多的上下文,您在哪里键入的
init(){vehicles=[]}
?错误是说,在使用结构的任何值之前,需要先为结构中的所有内容设置一个值,但我不明白为什么这里会这样说。我之前的评论是错误的,我在ForEach中使用了
id:\.self
。您的解决方案是一流的,非常感谢您的帮助。您是对的,非常感谢您的时间。我遇到以下错误:
无法在属性初始值设定项中使用实例成员“vehicles”;属性初始值设定项在“self”可用之前运行
。我是这样初始化的:
init(){vehicles=[]}
可能有什么问题?我需要更多的上下文,您在哪里键入的
init(){vehicles=[]}
?错误是说,在使用结构的任何值之前,需要先为结构中的所有内容设置一个值,但我不明白为什么这里会这样说。我之前的评论是错误的,我在ForEach中使用了
id:\.self
。您的解决方案是一流的,非常感谢您的帮助。