Swift 具有4个可选字符串的对象?具有加权值的属性,如何返回与一组属性最匹配的对象
所以一开始这似乎是一个非常直截了当的问题,但我发现了一些小问题,我目前的最终解决方案非常难看,所以我很好奇你们能想出什么 我有一个类,Swift 具有4个可选字符串的对象?具有加权值的属性,如何返回与一组属性最匹配的对象,swift,algorithm,match,matching,Swift,Algorithm,Match,Matching,所以一开始这似乎是一个非常直截了当的问题,但我发现了一些小问题,我目前的最终解决方案非常难看,所以我很好奇你们能想出什么 我有一个类,OptionalObject。 它有四个属性 let prop1: String? let prop2: String? let prop3: String? let prop4: String? 然后我有一个由一千个OptionalObject对象组成的数组,我保证没有两个对象具有完全相同的属性 例:(可选对象:prop1、prop2、prop3、prop4)
OptionalObject
。
它有四个属性
let prop1: String?
let prop2: String?
let prop3: String?
let prop4: String?
然后我有一个由一千个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个字符串(非可选)
我想根据这四个属性找到与我的具体对象
最匹配的OptionalObject
我可以想到两种方法,一种是对所有可选对象使用filter
,另一种是手动枚举所有可选对象,使用嵌套的if
语句检查属性(实际上与filter
相同)
如果我要过滤,它可能是这样的:
let matchingOptionalObject = optionalObjectsArray.filter {return $0.prop1 == arg1 && $0.prop2 == arg2 && $0.prop3 == arg3 && $0.prop4 == arg4}
我现在有了一个匹配的物体,但前提是它是精确匹配的
如果我的ConcreteObject
有(DEF,123,你好,再见)
我不会得到任何匹配,因为没有任何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个属性,所以我们需要讨论的唯一nil
参数至少有10种不同的可能性,这真的很难看
这就是我目前拥有的,我有一堆丑陋的if
语句检查nil
参数的组合,然后相应地构造一个过滤器
查询
好吧,我们需要继续前进,所以我想我们可以解决这个问题,并在以后找到更好的解决方案,但还有一个要求使得这个解决方案不起作用
每个属性都有不同的权重
有多个匹配项时选择最佳匹配项的两条规则:
1) 选择具有最多匹配属性的匹配项
2) 如果匹配的属性数与匹配的属性数相同,请选择权重最高的匹配项
Prop1的重量最高
Prop2的性能最低
以ConcreteObject
为例:(DEF,123,Hello,再见)
匹配的可选对象
:
Obj3: nil, nil, Hello, Goodbye
Obj4: nil, nil, Hello, nil
Obj7: DEF, 123, nil, Goodbye
Obj8: DEF, nil, nil, nil
New1: nil, 999, nil, NiHao
New2: XYZ, 999, nil, nil
我们会选择Obj7
作为最佳匹配,因为它有3个匹配属性
但是,让我们举一个新的ConcreteObject
和一组新的OptionalObjects
的例子,我们有以下匹配:
我们新匹配的OptionalObjects
:
Obj3: nil, nil, Hello, Goodbye
Obj4: nil, nil, Hello, nil
Obj7: DEF, 123, nil, Goodbye
Obj8: DEF, nil, nil, nil
New1: nil, 999, nil, NiHao
New2: XYZ, 999, nil, nil
我们会选择New2
,因为尽管New1
和New2
都有两个匹配属性,New2
具有更高权重的匹配属性
因此,这是一个两难的问题。
我希望几年前在我的本科算法课上,我没有记住一些关键概念,有一些干净的解决方案(甚至可能是斯威夫特提供的),但我已经快智穷了——所以任何人的任何建议或见解都是非常受欢迎的数学,特别是基础代数,给你答案
您需要在OptionalObject实例上定义一个二进制关系,它是反对称的,可传递的,但不是connex。比如整数
签名如下:
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
}
在您的问题中,您已经指定了需要在此函数中实现的规则。但请注意,如果这些规则没有导致反对称、传递和非connex二元关系,那么您的问题就没有得到很好的定义:从数学上讲,您需要有定义总顺序的规则,才能获得可用的解
现在,您的OptionalObject实例集必须使用Swift标准func排序(by:(Element,Element)->Bool)进行排序:
optionalObjectsArray.sort(by: myRelation)
最后,返回集合中的第一个对象就是您要查找的对象:
let myBestMatch = OptionalObjectsArray.sort(by: myRelation).first()
这里有一个合理的解决方案。有关详细信息,请参见代码注释
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)
结果按所需顺序排序。排序中的第一个对象得分最高。哇,太快了,很高兴看到你还活着——几年前我非常活跃的时候,我到处都能看到你。谢谢你的回答,我现在要试试:)很漂亮,很容易实现,而且是一个有效的解决方案。谢谢,我建议使用reduce而不是sort。Reduce将为您提供一个n阶解决方案,排序是order n log n。您只需返回scoreL>scoreL
在比较函数中,元组(可比较元素)本身是可比较的。您还可以直接将可选值与非可选值进行比较:如果opt.prop1==self.prop1{…}
等等。我相信,因为OptionalObjects数组保证是唯一的,所以会有一个总订单。有道理,我认为@rmaddy用他的score
方法为我写了一个myRelation
实现。我认为我的问题是我试图做一个匹配的解决方案,而排序和选择先让方式更敏感或挑剔:排序()如果您的属性真的被称为prop1
,prop2
等,Swift中的方法将采用。您可能应该