首页 > 解决方案 > 如何检查由不同结构组成的两个数组是否具有相同的值?

问题描述

我有两个由不同结构组成的数组,它们在数组的不同位置都有相同的信息。

我尝试使用 if 语句创建一个双 for-in 循环来检查它们是否是相同的值,但是我在 PHP 等其他语言中执行此操作的方式不起作用。

请帮助我,这是我到目前为止所得到的:

单例一个文件

struct oneStruct {
    var oneTitle: String
    var onePic: UIImage?
    var oneCategory: String
}

class OneSingleton {
    static let sharedInstance = OneSingleton()
    private init() {}
    var oneArray: [OneStruct] = [//you get the gist, this is full of array stuff]
}

单例两个文件

struct TwoStruct {
    var twoName: String
    var twoPic: UIImage?
    var twoSubject: String
    var twoStuff1: String
    // etc. I can't get into much more here, sorry.
}

class TwoSingleton {
    static let sharedInstance = TwoSingleton()
    private init() {}
    var twoArray: [TwoStruct] = [//you get the gist, this is full of array stuff as well]
}

视图控制器

//the singleton stuff is needed

var oneArray = [OneSingleton.sharedInstance.oneArray]
var twoArray = [TwoSingleton.sharedInstance.twoArray]
var filterArray: [TwoStruct] = []

override func viewDidLoad() {
    super.viewDidLoad()

    for two in twoArray {
        for one in oneArray {
            if two == one {
                filterArray.append(contentsOf: two)
            }
        }
    }
}

它给了我一个编译错误

二元运算符“==”不能应用于“[TwoStruct]”和“[OneStruct]”类型的操作数

我希望这一切都有意义。

提前致谢。

标签: arraysswiftstruct

解决方案


It seems you have, for each of your types OneStruct and TwoStruct, one respective property that you would like to use for comparison; category and subject, respectively. So to start out, lets blueprint two protocols for these two properties:

protocol Categorizable {
    var category: String { get }
}

protocol Subjectable {
    var subject: String { get }
}

For the purpose of this example, minimal examples of OneStruct and TwoStruct would then conform to Categorizable and Subjectable, respectively:

struct OneStruct: Categorizable {
    var category: String
    init(_ category: String) { self.category = category }
}

struct TwoStruct: Subjectable {
    var subject: String
    init(_ subject: String) { self.subject = subject }
}

Now, we don't really want to talk about equality when comparing two different types, rather equivalent classes or bi-predicates over which instances of these two types can be compared. So let's define e.g. a bi-predicate that can be used to compare two instances conforming to Categorizable and Subjectable, respectively:

struct BiPredicate<T: Categorizable, U: Subjectable> {
    static func compare(_ lhs: T, _ rhs: U) -> Bool {
        return lhs.category == rhs.subject
    }
    static func compare(_ lhs: U, _ rhs: T) -> Bool {
        return compare(rhs, lhs)
    }
}

Based on your own example code, it seems that you want two filter an array of TwoStruct (namely, an array of a type conforming to Subjectable) based on whether, for each element in the array, there exist a member in an array of OneStruct (namely, an array of a type conforming to Categorizable) for which the equivalent class or bi-predicate holds true.

/// Returns `lhs` argument filtered based on its BiPredicate intersection
/// with the `rhs` argument.
/// Time complexity: O(n^2)
func filterByIntersect<T: Subjectable, U: Categorizable>(_ lhs: [T], over rhs: [U]) -> [T] {
    return lhs.filter { l in rhs.contains { r in BiPredicate.compare(l, r) } }
}

Which we may use as:

let oneArray = [OneStruct("foo"), OneStruct("bar"), OneStruct("baz"), OneStruct("bax")]
let twoArray = [TwoStruct("baxx"), TwoStruct("foo"), TwoStruct("bax")]

let result = filterByIntersect(twoArray, over: oneArray)
for twoElement in result { print(twoElement.subject) } /* foo
                                                          bax */

Now, this was mostly a somewhat interesting exercise, but in the end ended up in quite a lot of boilerplate code for something that should be quite a simple task. You could always drop the generics and just go with a simple function for testing a single equivalent class over OneStruct and TwoStruct:

func compareCategoryWithSubject(_ lhs: OneStruct, _ rhs: TwoStruct) -> Bool {
    return lhs.category == rhs.subject
}

/// Returns `lhs` argument filtered based on intersection over comparing 
/// the `subject` properties of the elements of `lhs` with the `category`
/// properties of the elements of `rhs`.
/// Time complexity: O(n^2)
func filterByIntersect(_ lhs: [TwoStruct], over rhs: [OneStruct]) -> [TwoStruct] {
    return lhs.filter { l in rhs.contains { r in
        compareCategoryWithSubject(r, l) } }
}

Or, for a possibly even simple and also asymptotically more performant approach, placing all the logic into the filterByIntersect(...) function:

/// Returns `lhs` argument filtered based on intersection over comparing
/// the `subject` properties of the elements of `lhs` with the `category`
/// properties of the elements of `rhs`.
/// Time complexity: O(n) [contains(_:) is O(1) for Set)
func filterByIntersect(_ lhs: [TwoStruct], over rhs: [OneStruct]) -> [TwoStruct] {
    let rhsCategories = Set(rhs.map { $0.category })
    return lhs.filter { l in rhsCategories.contains(l.subject) }
}

Both the above yielding the same result as above:

let oneArray = [OneStruct("foo"), OneStruct("bar"), OneStruct("baz"), OneStruct("bax")]
let twoArray = [TwoStruct("baxx"), TwoStruct("foo"), TwoStruct("bax")]

let result = filterByIntersect(twoArray, over: oneArray)
for twoElement in result { print(twoElement.subject) } /* foo
                                                          bax */

推荐阅读