I have multiple sets of two arrays like that. I am getting them from a 3rd party.
var array1 : [Any?]
var array2 : [Any?]
I know about types of objects in these array (in compile time). As example that first element will be a String and second is an Int.
I currently compare each set of arrays like that (please notice that arrays aren't homogeneous).
array1[0] as? String == array2[0] as? String
array1[1] as? Int == array2[1] as? Int
...
The biggest problem that each set have different types in it. As result, I have let say 10 sets of arrays with 5 elements in each. I had to do 10*5 explicit conversion to specific type and comparation.
I want to be able to write a common method which can compare two arrays like that (without a need to specify all the types)
compareFunc(array1, array2)
I started to go down the road. However, I can't figure out what should I cast the objects too. I tried Equatable. However, swift doesn't like direct usage of it. So, something like that doesn't work
func compare(array1: [Any?], array2: [Any?]) {
for index in 0..<array1.count {
if (array1[index] as? Equatable != array2[index] as? Equatable) {
// Do something
}
}
}
Each object in this array will be Equatable. However, I am not sure how to use it in this case.
Construct a simple element-by-element comparison function based on (attempted) type conversion to known element types
Since you're aiming to compare arrays of (optional) Any elements, you could just construct a function that performs element-by-element comparison by using a switch block to attempt to downcast the elements of the array to different known types in your "3rd party arrays". Note that you needn't specify which element position that corresponds to a specific type (as this might differ between different set of arrays), just the exhaustive set of the different types that any given element might be of.
An example of such a function follows below:
func compareAnyArrays(arr1: [Any?], _ arr2: [Any?]) -> Bool {
/* element-by-element downcasting (to known types) followed by comparison */
return arr1.count == arr2.count && !zip(arr1, arr2).contains {
/* note that a 'true' below indicates the arrays differ (i.e., 'false' w.r.t. array equality) */
if let v1 = $1 {
/* type check for known types */
switch $0 {
case .None: return true
case let v0 as String:
if let v1 = v1 as? String { return !(v0 == v1) }; return true
case let v0 as Int:
if let v1 = v1 as? Int { return !(v0 == v1) }; return true
/* ...
expand with the known possible types of your array elements
... */
case _ : return true
/* */
}
}
else if let _ = $0 { return true }
return false
}
}
or, alternative, making the switch block a little less bloated by making use of (a slightly modified of) the compare(...) helper function from #Roman Sausarnes:s answer
func compareAnyArrays(arr1: [Any?], _ arr2: [Any?]) -> Bool {
/* modified helper function from #Roman Sausarnes:s answer */
func compare<T: Equatable>(obj1: T, _ obj2: Any) -> Bool {
return obj1 == obj2 as? T
}
/* element-by-element downcasting (to known types) followed by comparison */
return arr1.count == arr2.count && !zip(arr1, arr2).contains {
/* note also that a 'true' below indicates the arrays differ
(=> false w.r.t. equality) */
if let v1 = $1 {
/* type check for known types */
switch $0 {
case .None: return true
case let v0 as String: return !compare(v0, v1)
case let v0 as Int: return !compare(v0, v1)
/* ...
expand with the known possible types of your array elements
... */
case _ : return true
/* */
}
}
else if let _ = $0 { return true }
return false
}
}
Example usage:
/* Example usage #1 */
let ex1_arr1 : [Any?] = ["foo", nil, 3, "bar"]
let ex1_arr2 : [Any?] = ["foo", nil, 3, "bar"]
compareAnyArrays(ex1_arr1, ex1_arr2) // true
/* Example usage #2 */
let ex2_arr1 : [Any?] = ["foo", nil, 2, "bar"]
let ex2_arr2 : [Any?] = ["foo", 3, 2, "bar"]
compareAnyArrays(ex2_arr1, ex2_arr2) // false
This is a marginal solution, but it should reduce some of the duplicative code that you are trying to avoid:
func compareAnyArray(a1: [Any?], _ a2: [Any?]) -> Bool {
// A helper function for casting and comparing.
func compare<T: Equatable>(obj1: Any, _ obj2: Any, t: T.Type) -> Bool {
return obj1 as? T == obj2 as? T
}
guard a1.count == a2.count else { return false }
return a1.indices.reduce(true) {
guard let _a1 = a1[$1], let _a2 = a2[$1] else { return $0 && a1[$1] == nil && a2[$1] == nil }
switch $1 {
// Add a case statement for each index in the array:
case 0:
return $0 && compare(_a1, _a2, t: Int.self)
case 1:
return $0 && compare(_a1, _a2, t: String.self)
default:
return false
}
}
}
That isn't the most beautiful solution, but it will reduce the amount of code you have to write, and it should work for any two [Any?], as long as you know that the type at index 0 is an Int, and the type at index 1 is a String, etc...
A compact and Swift 5 version of great solution of Aaron Rasmussen:
func compare(a1: [Any?], a2: [Any?]) -> Bool {
guard a1.count == a2.count else { return false }
func compare<T: Equatable>(obj1: Any, _ obj2: Any, t: T.Type) -> Bool {
return obj1 as? T == obj2 as? T
}
return a1.indices.reduce(true) {
guard let _a1 = a1[$1], let _a2 = a2[$1] else { return $0 && a1[$1] == nil && a2[$1] == nil }
switch $1 {
case 0: return $0 && compare(obj1: _a1, _a2, t: Int.self)
case 1: return $0 && compare(obj1: _a1, _a2, t: String.self)
case 2: return $0 && compare(obj1: _a1, _a2, t: <#TypeOfObjectAtIndex2#>.self)
default: return false
}
}
}
Related
I want to compare the elements of two arrays and check if they are equal.
I already tried various solutions but nothing really works.
I tried the solution from
How to compare two array of objects?
This is my object:
struct AccountBalance: Decodable {
let balance: Double
let currency: String
init(balance: Double, currency: String ) {
self.currency = currency
self.balance = balance
}
enum CodingKeys: String, CodingKey {
case currency = "Currency"
case balance = "Balance"
}
}
This is the code from the link I tried:
let result = zip(accountBalance, getsSaved).enumerate().filter() {
$1.0 == $1.1
}.map{$0.0}
But I get this error:
Closure tuple parameter '(offset: Int, element: (AccountBalance, AccountBalance))' does not support destructuring with implicit parameters
Array provides a function elementsEqual which is able to compare two arrays without explicitly conforming to Equatable:
let result = accountBalance.elementsEqual(getsSaved) {
$0.balance == $1.balance && $0.currency == $1.currency
}
Edit:
If you want to have the equality result regardless of the order of objects in the array then you can just add sort with each of the arrays.
let result = accountBalance.sorted { $0.balance < $1.balance }.elementsEqual(getsSaved.sorted { $0.balance < $1.balance }) {
$0.balance == $1.balance && $0.currency == $1.currency
}
I am guessing two arrays should be considered equal when they contain the same elements, regardless of ordering.
First, implement Equatable and Hashable.
I am using hashValue as an id so I can sort the arrays first.
Here is what your AccountBalance class should look like:
struct AccountBalance: Decodable, Equatable, Hashable {
// Important parts!
var hashValue: Int{
return balance.hashValue ^ currency.hashValue &* 1677619
}
static func == (lhs: AccountBalance, rhs: AccountBalance) -> Bool{
return lhs.balance == rhs.balance && lhs.currency == rhs.currency
}
}
Then create an algorithm that sorts the ararys and then check each elements by one by by if the contents are the same.
Here is the function that take use of Equatable and Hashable.
func isEqual(arr1: [AccountBalance], arr2: [AccountBalance]) -> Bool{
if arr1.count != arr1.count{
return false
}
let a = arr1.sorted(){
$0.hashValue > $1.hashValue
}
let b = arr2.sorted(){
$0.hashValue > $1.hashValue
}
let result = zip(a, b).enumerated().filter() {
$1.0 == $1.1
}.count
if result == a.count{
return true
}
return false
}
I will suggest you implement the accepted answer of the link you provided because it controls that both arrays are the same size and it orders them.
But if you want that your code works I solved like this:
In order to have control of the comparison your struct should implement the Equatable protocol and overload the operator ==
extension AccountBalance : Equatable {}
func ==(lhs: AccountBalance, rhs: AccountBalance) -> Bool {
return lhs.balance == rhs.balance && lhs.currency == rhs.currency
}
Then compare both arrays and check if contains false, if it does, one or more items in the array aren't the same.
let result = !zip(accountBalance, getsSaved).enumerated().map() {
$1.0 == $1.1
}.contains(false)
Hope it helps you
I'm not sure about what the rest of your code does, but making the parameters explicit does make XCode happy:
let result = zip(accountBalance, getsSaved).enumerated().filter() { (arg) -> Bool in
let (_, (balance1, balance2)) = arg
return balance1.balance == balance2.balance
}.map{ $0.0 }`
Tried (_, (balance1, balance2)) -> Bool in directly, but it wouldn't let me either
Sample with several arrays:
import Foundation
let arr1: [String?] = ["word", nil, "word3", "word4"]
let arr2: [Double?] = [1.01, 1.02, nil, 1.04]
let arr3: [Int?] = [nil, 2, 3, 4]
var tuple1: [(title: String, number: String, coord: String)] = []
let countArray = arr1.count
for element in 0..<countArray {
tuple1.append((arr1[element].map
{String($0)} ?? "", arr2[element].map
{String($0)} ?? "", arr3[element].map
{String($0)} ?? ""))
}
print(tuple1)
result of printing:
[(title: "word1", number: "1.01", coord: ""), (title: "", number: "1.02", coord: "2"), (title: "word3", number: "", coord: "3"), (title: "word4", number: "1.04", coord: "4")]
I am attempting to sort an array which includes variables which are nil.
class TestArray {
var a: String? = nil
var b: String? = nil
init(a: String?, b: String?) {
self.a = a
self.b = b
}
}
let first = TestArray(a: "xxx", b: "yyy")
let second = TestArray(a: "zzz", b: "zzz")
let third = TestArray(a: "aaa", b: nil)
var array = [first, second, third]
array.sort(by: {($0.a as String!) > ($1.a as String!)})
array.sort(by: {($0.b as String!) > ($1.b as String!)}) // Throws an error
How can I sort by b and leave the third TestArray with b = nil at the end of the array?
Also I would like to sort through all b and then for the arrays in which b = nil sort the remaining by a.
I believe this should cover the situations you want without having to iterate more than once (which is expensive)
array.sort { (lessThan, item) -> Bool in
switch (lessThan.a, lessThan.b, item.a, item.b) {
// if bs exist, use that comparison
case (_, .some(let lessThanB), _, .some(let itemB)):
return lessThanB < itemB
// if bs don't exist by as do, use that comparison
case (.some(let lessThanA), .none, .some(let itemA), .none):
return lessThanA < itemA
// if one item has a value but the other doesn't, the item with the value should be first
case (_, .some(_), _, .none), (.some(_), _, .none, _ ):
return true
default:
return false
}
}
Here's (in my opinion) the best way:
First, I define a < operator for String? instances.
It operates for the specification that nil compares as less than any other string, so it'll appear first in sorting order.
fileprivate func <(a: String?, b: String?) -> Bool {
switch (a, b) {
case (nil, nil): return false
case (nil, _?): return true
case (_?, nil): return false
case (let a?, let b?): return a < b
}
}
Next, I wrote a sorting predicate that uses that operator. It works for the specification that instances are to be sorted first by their a. If those are equal, ties are broken by sorting by their b.
struct TestStruct {
var a: String?
var b: String?
}
let input = [
TestStruct(a: "xxx", b: "yyy"),
TestStruct(a: "zzz", b: "zzz"),
TestStruct(a: "aaa", b: nil)
]
let output = input.sorted { lhs, rhs in
if lhs.a != rhs.a { return lhs.a < rhs.a } // first sort by a
if lhs.b != rhs.b { return lhs.b < rhs.b } // then sort by b
return true
}
print(output)
//[
// TempCode.TestStruct(a: Optional("aaa"), b: nil),
// TempCode.TestStruct(a: Optional("xxx"), b: Optional("yyy")),
// TempCode.TestStruct(a: Optional("zzz"), b: Optional("zzz"))
//]
*I made TestStruct a struct just so I can use the synthesized initializer and automatic print behaviour. It'll work just the same if it's a class.
If you'll be comparing TestStruct a lot, and it makes sense for there to be a standard order to comparing it, then it's best you add conformance to Comparable. Doing so also requires conformance to Equatable:
extension TestStruct: Equatable {
public static func ==(lhs: TestStruct, rhs: TestStruct) -> Bool {
return
lhs.a == rhs.a &&
lhs.b == rhs.b
}
}
extension TestStruct: Comparable {
public static func <(lhs: TestStruct, rhs: TestStruct) -> Bool {
if lhs.a != rhs.a { return lhs.a < rhs.a } // first sort by a
if lhs.b != rhs.b { return lhs.b < rhs.b } // then sort by b
return true
}
}
Now you can sort your array easily with the sorting behaviour specified by TestStruct:
let output = input.sorted()
You can see it in action here.
In case you needn't necessarily sort in place, you could simply apply the "algorithm" you describe in a brute-force fashion to sort array using several filter followed sorted for the subset arrays.
"Algorithm":
Primarily sort by b property, given that it is not nil.
For elements where the b property is nil, sort by a property.
Leave elements where both a and b are nil at the end of the sorted array.
E.g.:
// lets set up a more interesting example
let first = TestArray(a: "xxx", b: "yyy")
let second = TestArray(a: "ccc", b: nil)
let third = TestArray(a: "aaa", b: nil)
let fourth = TestArray(a: "zzz", b: "zzz")
let fifth = TestArray(a: "bbb", b: nil)
var array = [first, second, third, fourth, fifth]
let sorted = array.filter { $0.b != nil }.sorted { $0.b! < $1.b! } +
array.filter { $0.b == nil && $0.a != nil }.sorted { $0.a! < $1.a! } +
array.filter { $0.b == nil && $0.a == nil }
sorted.enumerated().forEach { print("\($0): b: \($1.b), a: \($1.a)") }
/* 0: b: Optional("yyy"), a: Optional("xxx")
1: b: Optional("zzz"), a: Optional("zzz")
2: b: nil, a: Optional("aaa")
3: b: nil, a: Optional("bbb")
4: b: nil, a: Optional("ccc") */
Given an array defined as follow
let list: [Any]
I want to sort it WHEN
all the values inside it have the same type Element
AND Element is Comparable.
When it should return the sorted array
So I would need a function that when the array is populated in a way like the followings
let list: [Any] = [10, 11, 0, 2, -1]
let list: [Any] = ["Red", "Green", "Blue"]
let list: [Any] = [true, false, true, true]
does return the sorted array.
When it should return nil
On the other hand when list contains one of the following examples
let list: [Any] = [CGPointZero, CGPoint(x:1, y:1)] // CGPoint is not comparable
let list: [Any] = [10, "Hello"] // Values of different types
I want nil as return value.
Any idea?
Compile time solution
extension _ArrayType where Generator.Element == Any {
func sortQ() -> Any? {
return nil
}
}
extension _ArrayType where Generator.Element: Comparable {
func sortQ() -> [Self.Generator.Element] {
return self.sort(<)
}
}
// Because Bool is not comparable by default...
extension Bool: Comparable {
}
public func < (lhs: Bool, rhs: Bool) -> Bool {
return !lhs && rhs // or Int(lhs) < Int(rhs)
}
[10, 11, 0, 2, -1].sortQ() //[-1, 0, 2, 10, 11]
["Red", "Green", "Blue"].sortQ() //["Blue", "Green", "Red"]
[true, false, true, true].sortQ() //[false, true, true, true]
[CGPointZero, CGPoint(x:1, y:1)].sortQ() //nil
[10, "Hello"].sortQ() //nil
Runtime solutions:
UPDATE
Here is non final state. The problem is with casting to comparable. IMHO it is not possible. Until now I didn't know about trick with optional type. Anyway even casting of meta type is not possible because type is not known until runtime. My weak workaround is to list supported comparable types:
extension _ArrayType {
func sortQ() -> [Generator.Element]? {
var arrayOK = true
let sortedArray = sort { (firstElement, secondElement) -> Bool in
guard arrayOK else {
return false
}
let f = Mirror(reflecting: firstElement)
let s = Mirror(reflecting: secondElement)
guard f.subjectType == s.subjectType else {
arrayOK = false
return false
}
switch String(f.subjectType) {
case "Int":
return (firstElement as! Int) < (secondElement as! Int)
case "String":
return (firstElement as! String) < (secondElement as! String)
case "Bool":
return (firstElement as! Bool) < (secondElement as! Bool)
default:
arrayOK = false
return false
}
}
return arrayOK ? sortedArray : nil
}
}
UPDATE 2
The second option is to have comparable protocol defined differently (AnyComparable). Unfortunately it means to create extensions for all Comparable types.
Otherwise there's no way, at compile-time, the compiler can find the correct function/operator (as it doesn't know the types ahead of time).
So you have two options:
if you had some idea of the types you were comparing and define
them explicitly (update 1).
Use interface which does not use Self
type (update 2).
IMHO there is no other solution
protocol AnyComparable {
func compareTo(second: Any) -> Bool
}
extension AnyComparable where Self: Comparable {
func compareTo(second: Any) -> Bool {
if let secondSameType = second as? Self {
return self < secondSameType
}
return false
}
}
extension Int: AnyComparable {
}
extension String: AnyComparable {
}
extension Bool: AnyComparable {
}
extension _ArrayType {
func sortQ() -> [Generator.Element]? {
var arrayOK = true
var wantedType: Any.Type?
let sortedArray = sort { (firstElement, secondElement) -> Bool in
guard arrayOK else {
return false
}
if wantedType == nil {
wantedType = Mirror(reflecting: firstElement).subjectType
}
guard let f = firstElement as? AnyComparable where wantedType == Mirror(reflecting: secondElement).subjectType else {
arrayOK = false
return false
}
return f.compareTo(secondElement)
}
return arrayOK ? sortedArray : nil
}
}
For the moment, I wrote a little extension to check if all the elements are of the same type (I will be working on this to check if can get a result):
extension _ArrayType where Generator.Element == Any{
func hasEqualTypeAndComparable()->Bool{
if self.count > 0{
let firstType = self.first?.dynamicType
for val in self{
if firstType != val.dynamicType{
return false
}
}
return self.first is Comparable
}
return false
}
}
Example:
//Example 1
var values:[Any] = [2,1,4,3,"Hola"]
values.hasEqualTypeAndComparable() // Print false
//Example 2
var values:[Any] = [2,1,4,3]
values.hasEqualTypeAndComparable() // Prints true
If your use case allows you to provide a hint to the compiler, you could specify a filter on the type of output that you want:
extension _ArrayType where Generator.Element == Any {
func filterByType<T: Comparable>() -> [T] {
var output = [T]()
for i in self {
if let j = i as? T {
output.append(j)
}
}
return output
}
}
If the input array does not contain any elements of the specified type then it will just return an empty array. If the type is not a Comparable, then the code won't event compile.
Example:
let list: [Any] = [10, "Hello", 3, false, "Foo", "Bar", 1] // Values of different types
var output = list.filterByType() as [Int]
output.sortInPlace()
I've run into a situation that I'm sure is not that uncommon. I have two arrays of objects that conform to a protocol and I want to check if they are the equal.
What I'd really like to do is this:
protocol Pattern: Equatable
{
func isEqualTo(other: Pattern) -> Bool
}
func ==(rhs:Pattern, lhs:Pattern) -> Bool
{
return rhs.isEqualTo(lhs)
}
extension Equatable where Self : Pattern
{
func isEqualTo(other: Pattern) -> Bool
{
guard let o = other as? Self else { return false }
return self == o
}
}
However, this leads to the compile error:
Error:(10, 30) protocol 'Pattern' can only be used as a generic constraint because it has Self or associated type requirements
Based on this post I realise that I need to lose the Equatable inheritance on my protocol and push it down onto the concrete 'Pattern' declarations. Though I really don't understand why. If I'm defining how the two objects are equal based on the protocol by overloading == there really is no issue as far as I can see. I don't even need to know the actual types or whether they are classes or structs.
Regardless, this is all well and good and I can now compare concretePattern.isEqualTo(otherConcretePattern) but the issue remains that I can no longer compare arrays of these objects like I can compare an array of a concrete type as array equality relies on overloading the == operator.
The best I've managed to do so far is glom an isEqualTo method onto CollectionType via an extension. This at least allows me to compare arrays. But frankly, this code stinks.
extension CollectionType where Generator.Element == Pattern
{
func isEqualTo(patterns:[Pattern]) -> Bool {
return self.count as? Int == patterns.count && !zip(self, patterns).contains { !$0.isEqualTo($1) }
}
}
Is there really no other way of doing this? Please tell me I'm missing something obvious.
I have two arrays of objects that conform to a protocol and I want to check if they are the equal.
So you want to say the two arrays are equal if all the elements in them are equal and the elements all conform to pattern. i.e.
If a, b, c and d are all things that conform to Pattern, you want
a == c
a != b
a != d
b != d
let array1: [Pattern] = [a, b, c]
let array2: [Pattern] = [a, b, a]
let array3: [Pattern] = [a, d, c]
array1 == array2 // true
array1 == array3 // false
The easiest way to do this is actually to define an equality operator for two arrays of patterns i.e.
protocol Pattern
{
func isEqualTo(other: Pattern) -> Bool
}
func ==(rhs: Pattern, lhs: Pattern) -> Bool
{
return rhs.isEqualTo(lhs)
}
func ==(lhs: [Pattern], rhs: [Pattern]) -> Bool
{
guard lhs.count == rhs.count else { return false }
var i1 = lhs.generate()
var i2 = rhs.generate()
var isEqual = true
while let e1 = i1.next(), e2 = i2.next() where isEqual
{
isEqual = e1 == e2
}
return isEqual
}
I defined two types that conform to Pattern and tried various equality compares and it all works
struct Foo: Pattern
{
let data: String
init(data: String)
{
self.data = data
}
func isEqualTo(other: Pattern) -> Bool
{
guard let other = other as? Foo else { return false }
return self.data == other.data
}
}
struct Bar: Pattern
{
let data: String
init(data: String)
{
self.data = data
}
func isEqualTo(other: Pattern) -> Bool
{
guard let other = other as? Bar else { return false }
return self.data == other.data
}
}
let a = Foo(data: "jeremyp")
let b = Bar(data: "jeremyp")
let c = Foo(data: "jeremyp")
let d = Foo(data: "jeremy")
let comp1 = a == c // true
let comp2 = a == b // false
let comp3 = a == d // false
let array1: [Pattern] = [a, b, c]
let array2: [Pattern] = [a, b, a]
let array3: [Pattern] = [a, d, c]
let comp4 = array1 == array2 // true
let comp5 = array1 == array3 // false
The Swift answer:
protocol _Pattern
{
func _isEqualTo(_other: Any) -> Bool?
}
extension _Pattern where Self: Pattern
{
func _isEqualTo(_other: Any) -> Bool?
{
return (_other as? Self).map({ self.isEqualTo($0) })
}
}
protocol Pattern: _Pattern, Equatable
{
func isEqualTo(other: Self) -> Bool
}
extension Pattern
{
func isEqualTo(other: _Pattern) -> Bool
{
return _isEqualTo(other) ?? false
}
}
func == <T: Pattern>(rhs: T, lhs: T) -> Bool
{
return rhs.isEqualTo(lhs)
}
This is a pattern (pun intended) that I developed myself, and it works extremely well for situations like these. _Pattern is an auto-implemented protocol courtesy of the new protocol-extension feature, and represents a type-erased version of Pattern.
enum EnumType {
case WithString(String)
}
var enums = [EnumType]()
enums.append(EnumType.WithString("A"))
enums.append(EnumType.WithString("B"))
enums.append(EnumType.WithString("C"))
enums.append(EnumType.WithString("D"))
enums.append(EnumType.WithString("E"))
enums.append(EnumType.WithString("F"))
How to filter my enums array to find the one with associated value equal C. What predicate do I need to use?
The filter function can either be invoked as a global function over an array, or as an instance method (I prefer the later as it is more OO).
It accepts a closure with one parameter (the element being evaluated) that return a boolean (indicating whether the element matches the required condition).
Since it's a simple closure in a clear situation it's ok to use the abbreviated form.
I guess that other "With" cases would be added to your enum, so you could go with something like:
let filteredArray = enums.filter {
switch $0 {
case let .WithString(value):
return value == "C"
default:
return false
}
}
That should do the trick in your example.
As someone already mentioned, for Swift > 2.0 there's if case statement available:
enums.filter {
if case .WithString("C") = $0 {
return true
}
return false
}
But, it doesn't look nice, especially if you are going to repeat same logic again. What we can do here is to make EnumType conform to Equatable:
extension EnumType: Equatable {
}
func ==(lhs: EnumType, rhs: EnumType) -> Bool {
switch (lhs, rhs) {
case (.WithString(let lStr), .WithString(let rStr)):
return lStr == rStr
}
}
And now you can just:
enums.filter { $0 == .WithString("C") }
You could try adding a simple extension with a computed property to your enum and filter to that property:
extension EnumType {
var isC: Bool {
switch self {
case .WithString(let message): return message == "C"
default: return false
}
}
}
After this, you could simpy use the filtering as usual:
enums.filter { $0.isC }
var filteredArray = enums.filter { element in
switch element {
case EnumType.WithString(let string):
return string == "A"
default:
return false
}
}
This can probably be simplified with Swift 2.0 binding of assosciated values in if statements.
Inspired by #Jessy and SwiftLee, here is my solution:
// -----------------------
// CaseReflectable
// -----------------------
// designed for enums only
// (use it on other types not recommended)
protocol CaseReflectable {}
// default behaviors.
extension CaseReflectable {
/// case name
var caseName: String {
let mirror = Mirror(reflecting: self)
// enum cases:
// - normal case: no children
// - case with associated values: one child (with label)
guard let label = mirror.children.first?.label else {
return "\(self)" // normal case
}
// case with associated values
return label
}
/// associated values
var associatedValues: Any? {
// if no children, a normal case, no associated values.
guard let firstChild = Mirror(reflecting: self).children.first else {
return nil
}
return firstChild.value
}
}
// --------------------------
// custom operator ~=
// --------------------------
/// match enum cases with associated values, while disregarding the values themselves.
/// usage: `Enum.enumCase ~= instance`
func ~= <Enum: CaseReflectable, AssociatedValue>(
// an enum case (with associated values)
enumCase: (AssociatedValue) -> Enum, // enum case as function
// an instance of Enum
instance: Enum
) -> Bool
{
// if no associated values, `instance` can't be of `enumCase`
guard let values = instance.associatedValues else { return false }
// if associated values not of the same type, return false
guard values is AssociatedValue else { return false }
// create an instance from `enumCase` (as function)
let case2 = enumCase(values as! AssociatedValue)
// if same case name, return true
return case2.caseName == instance.caseName
}
// ------------
// Enum
// ------------
// enum with associated values
// (conforms to `CaseReflectable`)
enum Enum: CaseReflectable {
case int(Int)
case int2(Int)
case person(name: String, age: Int)
case str(String)
}
// ------------
// main
// ------------
let a: Enum = .int(3)
Enum.int ~= a // true
Enum.int2 ~= a // false
let joe = Enum.person(name: "joe", age: 8)
Enum.person ~= joe // true
Enum.int ~= joe // false
// array of enum cases
let items: [Enum] = [
.int(1), .str("hi"), .int(2)
]
// filter enum cases
let filtered = items.filter { Enum.int ~= $0 }
print(filtered) // [Enum.int(1), Enum.int(2)]
You can achieve something more reusable by implementing Equatable protocol:
enum EnumType {
case WithString(String)
}
extension EnumType: Equatable {
static func ==(lhs: EnumType, rhs: String) -> Bool {
switch lhs {
case .WithString(let value):
return value == rhs
}
}
}
EnumType.WithString("F") == "A" // false
EnumType.WithString("F") == "F" // true