Объекты с 4 необязательными String?свойства с взвешенным значением, как вернуть объект с наилучшим соответствием набору свойств - PullRequest
0 голосов
/ 31 мая 2018

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

у меня есть класс, OptionalObject.У него четыре свойства:

let prop1: String?
let prop2: String?
let prop3: String?
let prop4: String?

У меня есть массив из тысячи этих OptionalObject объектов, и я гарантирую, что нет двух объектов с одинаковыми свойствами.

EX: (OptionalObject: prop1, prop2, prop3, prop4

Obj1: ABC, 123, Hello, Goodbye
Obj2: DEF, 456, nil, nil
Obj3: nil, nil, Hello, Goodbye
Obj4: nil, nil, Hello, nil
Obj5: ABC, nil, nil, nil
Obj6: ABC, nil, Hello, Goodbye
Obj7: DEF, 123, nil, Goodbye
Obj8: DEF, nil, nil, nil
...

, а затем у меня есть отдельный объект другого класса, который имеет все 4 строки (необязательно)

(ConcreteObject: arg1: String, arg2: String, arg3: String, arg4: String)

Я хочу найти лучшее совпадение OptionalObject с моим ConcreteObject, основанное на этих четырех свойствах.

Что я могу придумать для двух способов сделать это, один из них состоит в том, чтобыиспользуйте filter на всех OptionalObjects, другой - вручную перечислять все OptionalObjects, иметь вложенные операторы if для проверки свойств (что фактически равно filter в любом случае)

ЕслиЯ должен был отфильтровать, это может быть что-то вроде этого:

let matchingOptionalObject = optionalObjectsArray.filter {return $0.prop1 == arg1 && $0.prop2 == arg2 && $0.prop3 == arg3 && $0.prop4 == arg4}

и бум У меня теперь есть один объект, который соответствует ... но только если это точное соответствие.

Если мой ConcreteObject имеет (DEF, 123, Hello, Goodbye) Я бы не получил ни одного совпадения, потому что нет OptionalObjects совпадения точно.

Я бы ожидалвозврат Obj3, Obj4, Obj7, Obj8


Итак, естественно, я думаю, хорошо, давайте сначала посмотрим, какие аргументы у нас не как nil, а затем сконструируем запрос соответственно.

Для этогоНапример, я собираюсь притвориться, что есть только два свойства, поэтому их легче понять:

let matchingOptionalObject = optionalObjectsArray.filter { 
    if $0.prop1 != nil {
        if $0.prop2 != nil {
            return $0.prop1 == arg1 && $0.prop2 == arg2
        } else {
            return $0.prop1 == arg1
        }
    } else {
        if $0.prop2 != nil {
            return $0.prop2 == arg2
        } else {
            return false
        }
    }
}

Но тогда проблема в том, что, поскольку существует 4 свойства, существует как минимум 10 различных возможностей уникальности nil аргументов, которые нам нужно охватить, и это становится действительно очень уродливым

Что у меня сейчас есть, у меня есть куча уродливых if утверждений, проверяющих комбинацию nil аргументов, и затемпостроение запроса filter соответственно ...


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

. Это означает, что каждое свойство имеет различный вес.

Два правила выбора лучшего маtch при наличии нескольких совпадений:
1) Выберите совпадение, у которого наибольшее количество совпадающих свойств
2) Если совпадения имеют одинаковое количество совпадающих свойств, выберите совпадение с наибольшим весом.

Prop1 имеет наибольший вес
Prop2 имеет наименьший

Итак, с примером ConcreteObject: (DEF, 123, Hello, Goodbye)

Соответствует OptionalObjects:

Obj3: nil, nil, Hello, Goodbye
Obj4: nil, nil, Hello, nil
Obj7: DEF, 123, nil, Goodbye
Obj8: DEF, nil, nil, nil

Мы бы выбрали Obj7 в качестве наилучшего совпадения, потому что у него есть 3 свойства сопоставления


Но, скажем, для другого примера с новым ConcreteObject и новым набором OptionalObjects, у нас есть это совпадение:

Наш новый Matched OptionalObjects:

New1: nil, 999, nil, NiHao
New2: XYZ, 999, nil, nil

Мы бы выбрали New2, потому что, хотя New1 и New2 оба имеют 2 совпадающих свойства, New2 имеет соответствующие свойства с более высоким весом.


Итак, возникает дилемма.
Я надеюсь, что просто не помню некоторые ключевые понятия много лет назад в моих алгоритмах старшекурсниковкласс, и что есть какое-то чистое решение (может быть, даже то, что Swiftобеспечивает), но я почти сошел с ума - так что любые предложения или идеи, которые есть у кого-либо, более чем приветствуются

Ответы [ 2 ]

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

Вот одно разумное решение.Подробности см. В комментариях к коду.

struct OptionalObject {
    let prop1: String?
    let prop2: String?
    let prop3: String?
    let prop4: String?
}

struct ConcreteObject {
    let prop1: String
    let prop2: String
    let prop3: String
    let prop4: String

    // Determine the score.
    // "matches" counts the number of matching properties.
    // "weight" gives 8 for the 1st property, 4 for the 2nd, 2 for the 3rd, 1 for the 4th. Adjust to suit your needs
    func score(for opt: OptionalObject) -> (matches: Int, weight: Int) {
        var matches = 0
        var weight = 0
        if opt.prop1 == self.prop1 { matches += 1; weight += 8 }
        if opt.prop2 == self.prop2 { matches += 1; weight += 4 }
        if opt.prop3 == self.prop3 { matches += 1; weight += 2 }
        if opt.prop4 == self.prop4 { matches += 1; weight += 1 }

        return (matches, weight)
    }

    // Compares two OptionalObject by getting the score of each
    // against "self".
    func compare(lhs: OptionalObject, rhs: OptionalObject) -> Bool {
        let scoreL = score(for: lhs)
        let scoreR = score(for: rhs)

        // If the number of matches are the same, compare the weight
        return scoreL > scoreR
    }
}

// Test ConcreteObject    
let concrete = ConcreteObject(prop1: "DEF", prop2: "123", prop3: "Hello", prop4: "Goodbye")

// List of OptionalObject
var optionals: [OptionalObject] = [
    OptionalObject(prop1: nil, prop2: nil, prop3: "Hello", prop4: nil),
    OptionalObject(prop1: "DEF", prop2: "456", prop3: nil, prop4: nil),
    OptionalObject(prop1: "ABC", prop2: "123", prop3: "Hello", prop4: "Goodbye"),
    OptionalObject(prop1: nil, prop2: nil, prop3: "Hello", prop4: "Goodbye"),
    OptionalObject(prop1: "DEF", prop2: "456", prop3: "Hello", prop4: "Goodbye"),
    //OptionalObject(prop1: nil, prop2: nil, prop3: nil, prop4: nil),
]

// Sort the list based on the ConcreteObject
let sorted = optionals.sorted { concrete.compare(lhs: $0, rhs: $1) }
print(sorted)

Результаты отсортированы в нужном порядке.Первый объект в sorted имеет наивысший балл.

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

Математика, и в частности базовая алгебра, дает вам ответ.

Вам необходимо определить бинарное отношение в ваших экземплярах OptionalObject, которое является антисимметричным, транзитивным, но не связным.Как <для целых чисел. </p>

Вот подпись:

func myRelation(_ o1: OptionalObject, _ o2: OptionalObject) -> Bool {
  // compare the 4 member properties of o1 and o2, using weights if not nil
  // return true if o1 matches best, otherwise return false
}

В своем вопросе вы указали правила, которые необходимо реализовать в этой функции.НО ЗАМЕЧАНИЕ: если правила не приводят к антисимметричному, транзитивному и не связному двоичному отношению, ваша проблема не будет четко определена: вам нужно иметь правила, определяющие порядок total , для решения доступно,математически говоря.

Теперь ваш набор экземпляров OptionalObject должен быть упорядочен с использованием стандартной Swift func sort (by: (Element, Element) -> Bool) :

optionalObjectsArray.sort(by: myRelation)

Наконец, первый объект в возвращенной коллекции - это тот, который вы искали:

let myBestMatch = OptionalObjectsArray.sort(by: myRelation).first()
...