Фильтр между двумя массивами объектов, избегая вложенного цикла for - PullRequest
0 голосов
/ 02 мая 2018

В Swift 4 как преобразовать вложенный цикл for, проверяющий равенство только одного свойства, в фильтр?

Базовый пример:

// Basic object
struct Message {
    let id: String
    let content: String

    init(id: String, content: String) {
        self.id = id
        self.content = content
    }
}

// Array of objects
let local = [Message.init(id: "1234", content: "test1"), Message.init(id: "2345", content: "test2")]

// Array of objects, one has updated content
let server = [Message.init(id: "1234", content: "testDiff1"), Message.init(id: "3456", content: "test3")]

var foundList = [Message]()

// Nested loop to find based on one property matching
for i in local {
    for j in server {
        if i.id == j.id {
            foundList.append(i)
        }
    }
}

Это работает, как и ожидалось (foundList содержит local [0]), но кажется, что для этого должен быть быстрый способ?

Ответы [ 3 ]

0 голосов
/ 02 мая 2018

Используйте filter.

import Foundation

struct Message: Equatable {
    let id: String
    let content: String
    static func == (lhs: Message, rhs: Message) -> Bool {
        return lhs.id == rhs.id
    }
}

let local  = [ Message(id: "1234", content: "test1"),     Message(id: "2345", content: "test2") ]
let server = [ Message(id: "1234", content: "testDiff1"), Message(id: "3456", content: "test3") ]

let foundList = local.filter { server.contains($0) }

print(foundList) // prints [Message(id: "1234", content: "test1")]

Обратите внимание, что я удалил инициализатор и использую Message (...) вместо Message.init (...).

0 голосов
/ 02 мая 2018

Должно быть легко написать фильтр для этого. Я предполагаю, что вы хотите локальные сообщения, которые также находятся на сервере.

let m = local.filter 
{
    localMessage in 
    server.contains(where: { $0.id == localMessage.id })
}

Если у вас много сообщений, было бы неплохо создать набор интересных идентификаторов.

let filterIds = Set(server.map{ $0.id })
let m = local.filter { filterIds.contains($0) }

Это уменьшит сложность O, потому что вы не выполняете линейный поиск по множеству. Временная сложность для contains(where:) для массива составит O (n) , где n - количество элементов. Для набора Apple документирует сложность contains() как O (1) Конечно, существуют дополнительные затраты на создание набора и для небольших n линейный поиск может быть быстрее, чем установленный доступ.

0 голосов
/ 02 мая 2018

for петли могут быть переписаны одним for циклом + where условие:

for m in local where server.contains(where: { $0.id == m.id }) {
    foundList.append(m)
}

или объединить filter с contains:

foundList = local.filter { m in server.contains(where: { $0.id == m.id } }

P.S. Также, соответствие Message struct протоколу Equatable. Это позволяет упростить contains метод:

for m in local where server.contains(m) {
    foundList.append(m)
}

с использованием filter:

foundList = local.filter { server.contains($0) }
...