这里是不是存在更好的解决方案来处理 Swift 中的这个 api 问题?

Posted

技术标签:

【中文标题】这里是不是存在更好的解决方案来处理 Swift 中的这个 api 问题?【英文标题】:Did here existed a better solution to handle this api problem in Swift?这里是否存在更好的解决方案来处理 Swift 中的这个 api 问题? 【发布时间】:2018-11-08 17:38:53 【问题描述】:

我的原始 json 数据可能会误导你。键数组并不总是在同一索引处匹配其值。所以我重写了我的数据以反映我的意图。

假设我们有一个表格视图来显示带有其 json 的歌曲:


    "albums": [
        
            "title": "A",
            "id": "174172",
            "artistName": "Person X"
        ,
        
            "title": "B",
            "id": "19201827",
            "artistName": "Person Y"
        ,
        
            "title": "C",
            "id": "1927",
            "artistName": "Person Z"
        
    ],
    "songs": [
        
            "name": "Song A",
            "albumName": "A",
            "albumId": "174172",
            "duration": 180
        ,
        
            "name": "Song B",
            "albumName": "A",
            "albumId": "174172",
            "duration": 200
        ,
        
            "name": "Song C",
            "albumName": "B",
            "albumId": "19201827",
            "duration": 216
        ,
        
            "name": "Song D",
            "albumName": "C",
            "albumId": "1927",
            "duration": 216
        
    ]

我的架构是这样的:

struct Album: Decodable 
    let title: String
    let id: String
    let artistName: String


struct Song: Decodable 
    let name: String
    let albumName: String
    let albumId: String
    let duration: Int

视图控制器伪代码如下:

class ViewController: UIViewController 
    var songs: [Song] = []
    var albums: [Album] = []

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return songs.count
    

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableview.dequeueReusableCell(withIdentifier: "SongCell", for: indexPath) as! SongCell
        let song = songs[indexPath.row]
        let album = albums.first  $0.id == song.albumId 
        cell.updateUI(withSong: song, album: album)
        return cell
    

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        let song = songs[indexPath.row]
        let album = albums.first  $0.id == song.albumId 
        pushDetailSongViewController(song, album)
    

    func pushDetailSongViewController(_ song: Song, _ album: Album?) 
    

当我们的专辑歌曲太多时,let album = albums.first $0.id == song.albumId 会出现严重的性能问题。

那么我们应该在这里使用什么数据结构来处理更新性能?

【问题讨论】:

@LinusGeffarth 感谢您的回答。我认为我的原始问题可能会误导我现在更新所有 json。你会再读一遍吗? 我更新了我的答案。它和以前完全一样,除了我必须重命名几个字段。但是请注意,这将只匹配每张专辑的一首歌曲。您必须执行[Album: [Song]],以获取专辑字典以及属于每个专辑的所有歌曲。 所以,我还在我的答案中添加了[Album: [Song]] 功能。 【参考方案1】:

解析完键和值后,您可以将这两个数组组合成一个字典,然后让表视图的数据源成为该字典。

首先,使您的Song 结构符合Hashable 协议:

struct Song: Hashable 

为专辑和歌曲创建一个数组:

var albums: [Album] = []
var songs:  [Song]  = []

然后,将songs 数组缩减为字典,如下所示:

let data = songs.reduce([Album: Song]())  (result, song) -> [Album: Song] in
    guard let album = albums.first(where:  $0.id == song.albumID ) else  return result 
    return result.merging([album: song], uniquingKeysWith:  (first, _) in first )


我用两个演示数组对此进行了测试:

let albums = [Album(id: "1",     name: "one"), Album(id: "2",     name: "two"), Album(id: "3",     name: "three")]
let songs  = [Song(albumID: "1", name: "ONE"), Song(albumID: "2", name: "TWO"), Song(albumID: "3", name: "THREE")]

那些把data变成:

[
    <Album id: "1", name: "one">  : <Song albumID: "1", name: "ONE">,
    <Album id: "2", name: "two">  : <Song albumID: "2", name: "TWO">,
    <Album id: "3", name: "three">: <Song albumID: "3", name: "THREE">
]

额外积分

如果你想要每张专辑的所有歌曲,你必须让data[Album: [Song]]

let data = albums.reduce([Album: [Song]]())  (result, album) -> [Album: [Song]] in
    let _songs = songs.filter( $0.albumID == album.id )
    guard !_songs.isEmpty else  return result 
    return result.merging([album: _songs], uniquingKeysWith:  (first, _) in first )

使用以下数组:

let albums = [Album(id: "1",     name: "one"), Album(id: "2",     name: "two"), Album(id: "3",     name: "three")]
let songs  = [Song(albumID: "1", name: "ONE"), Song(albumID: "2", name: "TWO"), Song(albumID: "3", name: "THREE"),
              Song(albumID: "1", name: "ONE-1"), Song(albumID: "1", name: "ONE-2"), Song(albumID: "3", name: "THREE-1")]

...你会得到:

[
    <Album name: three, id: 3>: [
        <Song name: THREE, albumID: 3>
        <Song name: THREE-1, albumID: 3>
    ], 
    <Album name: one, id: 1>: [
        <Song name: ONE, albumID: 1>, 
        <Song name: ONE-1, albumID: 1>, 
        <Song name: ONE-2, albumID: 1>
    ],
    <Album name: two, id: 2>: [
        <Song name: TWO, albumID: 2>
    ]
]

【讨论】:

太棒了!谢谢!【参考方案2】:

JSON 解析完成后,您应该创建一个如下所示的struct

struct DataSet 
 let id: String
 let name: String
 let value: String

此外,查看您的 json,似乎 KeyValue 数组的相同索引处的对象与 idkey 相同。因此,在合并两个数组时,如果您迭代一个数组,您将知道另一个数组的索引 (O(1)) 。因此合并的时间复杂度为O(n)

【讨论】:

感谢您的回答。我认为我的原始问题可能会误导我现在更新所有 json。你会再读一遍吗?【参考方案3】:

如果你不想改变太多,也许 mapDictionary 会有所帮助:

    let keyMaps =  [String : String](uniqueKeysWithValues: keys.map($0.id, $0.name))
    keyNamesInSequenceSameWithValues =  values.map keyMaps[$0.key]! )

【讨论】:

感谢您的回答。我认为我的原始问题可能会误导我现在更新所有 json。你会再读一遍吗?

以上是关于这里是不是存在更好的解决方案来处理 Swift 中的这个 api 问题?的主要内容,如果未能解决你的问题,请参考以下文章

检查有向无环图中两个顶点之间是不是存在路径 - 查询

Swift 3 更好的方式来解开一个可选的

Swift 2 错误处理和 while

Android加速度计过滤?

在 Swift 语言中更好的处理 JSON 数据:SwiftyJSON

Swift 中是不是有 API 来确定 iOS 设备的处理器寄存器大小?