Набор объектов с необязательными свойствами String. Улучшение функции, принимающей аргументы String, и поиск наилучшего соответствия в наборе. - PullRequest
0 голосов
/ 25 февраля 2019

Мы пытаемся оптимизировать какой-то «взвешенный» алгоритм сопоставления, который мы использовали, и решили проконсультироваться в Интернете для получения дополнительных идей

У нас есть структура MyStruct с 5 дополнительными свойствами (в swift это просто означает, что свойство может иметь значение nil):

prop1: String?
prop2: String?
prop3: String?
prop4: String?
prop5: String?

, тогда у нас есть набор MyStruct (гарантируется, что не существует 2 экземпляров, которые имеют одинаковые точные свойства),

structArray: Set<MyStruct>

у нас есть функция, которая принимает этот массив, а также свойства 1-5 в словаре, чтобы вернуть 1 единственный экземпляр, который наилучшим образом соответствует.Если какое-либо из свойств не совпадает, экземпляр немедленно выводится из конфликта

func findBestMatch(forSet set:Set<MyStruct>, andArgs argDict:[String:String]) -> MyStruct? {
  //Will use this to store any matches, as well as an associated match score
  var bestMatch: MyStruct?
  var topScore = 0
  for element in set {
    var score = 0
    if let p1 = argDict["p1"] {
      if p1 == element.prop1 {
        score += 16 //p1 match has highest weight
      } else {
        continue
      }
    }

    if let p2 = argDict["p2"] {
      if p2 == element.prop2 {
        score += 8 //p2 match has second-highest weight
      } else {
        continue
      }
    }

    //etc for the other 3 properties

    if score > topScore {
      topScore = score
      bestMatch = element 
    }
  }
  return bestMatch
}

ПРИМЕР:

exInstance1
  prop1 = "no good"
  prop2 = nil
  prop3 = "goodbye

exInstance2
  prop1 = "hello"
  prop2 = "noproblem"
  prop3 = "goodbye"

exInstance3
  prop1 = nil
  prop2 = nil
  prop3 = "goodbye"

exampleSet: Set<MyStruct> = [exInstance1, exInstance2, exInstance3]

matchingProperties: [String:String] = {
  "p1": "hello",
  "p3": "goodbye"
}


findBestMatch(forSet: exampleSet, andArgs: matchingProperties)

exInstance1 имеет только 1 совпадение,на prop3, но поскольку prop1 вообще не совпадает, exInstance не получает оценку

exInstance2 для обоих свойств, а получает оценку 20

exInstance3 для одного свойства,и получает оценку 4

exInstance2 выбирается и возвращается


Вопрос: есть ли лучший способ сделать это?Если нет, есть ли способы улучшить этот алгоритм?

Ответы [ 2 ]

0 голосов
/ 26 февраля 2019

В этом случае возможны следующие варианты оптимизации:

  1. Не использовать словарь.Вместо этого передайте аргументы напрямую.
  2. На каждой итерации цикла используется необязательное связывание для каждого свойства.Этого можно избежать.
  3. Если все поля объекта не являются обязательными и равны тем, которые передаются в функцию, это лучше.

Исходя из вышеизложенного, я предлагаюследующее решение:

import Foundation

struct MyStruct {
    var prop1: String? = nil
    var prop2: String? = nil
    var prop3: String? = nil
}

extension MyStruct: Hashable {}

func findBestMatch(for set: Set<MyStruct>,
                   oProperty1: String? = nil,
                   oProperty2: String? = nil,
                   oProperty3: String? = nil) -> MyStruct?
{
    let mask000 = 0b00000000
    let mask001 = 0b00000001
    let mask010 = 0b00000010
    let mask011 = 0b00000011
    let mask100 = 0b00000100
    let mask101 = 0b00000101
    let mask110 = 0b00000110
    let mask111 = 0b00000111

    var mask = mask000

    if let _ = oProperty1 {
        mask |= mask001
    }
    if let _ = oProperty2 {
        mask |= mask010
    }
    if let _ = oProperty3 {
        mask |= mask100
    }

    if mask == mask000 {
        return nil
    } else if mask == mask001 {
        let prop3 = oProperty3!
        return set.first(where: { $0.prop3 == prop3 })
    } else if mask == mask010 {
        let prop2 = oProperty2!
        return set.first(where: { $0.prop2 == prop2 })
    } else if mask == mask011 {
        let prop2 = oProperty2!
        let prop3 = oProperty3!
        return set.first(where: { $0.prop2 == prop2 && $0.prop3 == prop3 })
    } else if mask == mask100 {
        let prop1 = oProperty1!
        return set.first(where: { $0.prop1 == prop1 })
    } else if mask == mask101 {
        let prop1 = oProperty1!
        let prop3 = oProperty3!
        return set.first(where: { $0.prop1 == prop1 && $0.prop3 == prop3 })
    } else if mask == mask110 {
        let prop1 = oProperty1!
        let prop2 = oProperty2!
        return set.first(where: { $0.prop1 == prop1 && $0.prop2 == prop2 })
    } else if mask == mask111 {
        let prop1 = oProperty1!
        let prop2 = oProperty2!
        let prop3 = oProperty3!
        return set.first(where: { $0.prop1 == prop1 && $0.prop2 == prop2 && $0.prop3 == prop3 })
    }
    return nil
}

let exInstance1 = MyStruct(prop1: "no good", prop2: nil, prop3: "goodbye")
let exInstance2 = MyStruct(prop1: "hello", prop2: "noproblem", prop3: "goodbye")
let exInstance3 = MyStruct(prop1: nil, prop2: nil, prop3: "goodbye")

let exampleSet: Set<MyStruct> = [
    exInstance1,
    exInstance2,
    exInstance3,
]

if let object = findBestMatch(for: exampleSet, oProperty1: "hello", oProperty2: nil, oProperty3: "goodbye") {
    print(object) // print MyStruct(prop1: Optional("hello"), prop2: Optional("noproblem"), prop3: Optional("goodbye"))
} else {
    print("not found")
}

Мы проанализируем шаг за шагом на вашем примере:

  1. mask = 000
  2. oProperty1 != nil.=> mask = mask | 100 = 000 | 100 = 100
  3. oProperty2 == nil.=> mask = 100
  4. oProperty3 != nil.=> mask = mask | 001 = 100 | 001 = 101
  5. (mask == mask101) == true.=>

    let prop1 = oProperty1!
    let prop3 = oProperty3!
    return set.first(where: { $0.prop1 == prop1 && $0.prop3 == prop3 })
    
0 голосов
/ 26 февраля 2019

Я бы увидел небольшое улучшение, только если вы исключите доступ к словарям вне цикла for, например

func findBestMatch(forSet set:Set<MyStruct>, andArgs argDict:[String:String]) -> MyStruct? {
    //Will use this to store any matches, as well as an associated match score
    var bestMatch: MyStruct?
    var topScore = 0
    let p1 = argDict["p1"]
    let p2 = argDict["p2"]  // and so on
    for element in set {
        var score = 0
        if let p1 = p1 {
            if p1 == element.prop1 {
                score += 16 //p1 match has highest weight
            } else {
                continue
            }
        }

        //etc for the other properties

        if score > topScore {
            topScore = score
            bestMatch = element
        }
    }
    return bestMatch
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...