Filter array of items by enum property with associated value

2k Views Asked by At
class MyClass: Decodable {

    let title: String?
    let type: MyClass.MyType?

    enum MyType {
        case article(data: [Article])
        case link(data: [LinkTile])
        case none
    }
}

I would like to filter an array of MyClass items, so the filtered array won't contain instances with type .none

let filteredArray = array.filter { $0.type != .none } // this doesn't work
2

There are 2 best solutions below

2
On BEST ANSWER

Unfortunately, you can't use == with enums with associated values. You need to use pattern matching, but that needs to be done in a switch or if statement.

So, that leads to something ugly like this:

let filteredArray = array.filter { if case .none = $0.type! { return false }; return true }

Notes:

  1. You can't name your enum Type because it conflicts with the built-in Type. Change it to something like MyType.

  2. It is terribly confusing to use none as a case in a custom enum because it gets confused (by the humans) with none in an optional. This is made worse by the fact that your type property is optional. Here I have force unwrapped it, but that is dangerous of course.

    You could do:

    if case .none? = $0.type
    

    This would match the none case explicitly and treat nil as something you want to keep.

    To filter out nil and .none, you could use the nil coalescing operator ??:

    if case .none = ($0.type ?? .none)
    

    I would suggest declaring type as MyClass.MyType instead of MyClass.MyType?.

0
On

I made you a simple example of how to use enum in your context with a filter function.

enum Foo {

  case article(data: [Int])
  case link(data: [String])
  case `none`

  static func myfilter(array: [Foo]) -> [Foo]{
    var newArray:[Foo] = []
    for element in array {
      switch element {
      case .article(let article):
        newArray.append(.article(data: article))
      case .link(let link):
        newArray.append(.link(data: link))
      case .none:
        break
      }
    }
    return newArray
  }
}

let foo: [Foo] = [.article(data: [1,2,3]), .link(data: ["hello", "world"]), .none]

print(Foo.myfilter(array: foo))

I made a code which you can compile and test, you have to change the type for Foo, articleand link.

When you want to use an enum, you have to use switch case.

If you absolutely want to use the filter in swift you can, but you need to implement the protocol Sequence which is more complicated in this case.

For each case of your enum, you have to manage a case which use the concept of pattern matching. It is very powerful.