Существовало ли здесь лучшее решение для решения этой проблемы API в Swift? - PullRequest
0 голосов
/ 08 ноября 2018

мои необработанные данные 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 } - это место с ужасной проблемой производительности.

Так какую структуру данных мы должны использовать здесь для обработки производительности обновления?

Ответы [ 3 ]

0 голосов
/ 08 ноября 2018

После анализа ключей и значений вы можете объединить два массива в словарь, а затем источником данных вашего табличного представления будет этот словарь.

Сначала приведите структуру 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>
    ]
]
0 голосов
/ 09 ноября 2018

Если вы не хотите слишком сильно изменять, возможно, mapDictionary поможет:

    let keyMaps =  [String : String](uniqueKeysWithValues: keys.map{($0.id, $0.name)})
    keyNamesInSequenceSameWithValues =  values.map{ keyMaps[$0.key]! )
0 голосов
/ 08 ноября 2018

Вы должны создать struct, как показано ниже, после разбора JSON.

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

Кроме того, глядя на ваш json, кажется, что объекты с одинаковыми индексами Key и Value массивов одинаковы по отношению к id и key. Таким образом, во время объединения обоих массивов, если вы выполните итерацию одного массива, вы будете знать индекс другого массива (O(1)). Следовательно, временная сложность слияния будет O(n).

...