Search code examples
swifttype-constraintstype-aliastype-extension

Class extension typealias's property type constraint


Mission: I need to provide an Array extension method which would compare 2 arrays of raw representables whose raw type conforms to Equatable and say if the arrays contain the same elements by reusing the below pieces of code.

What I have at the moment:

public extension Array {
  func containsTheSameElements(as array: [Element], condition: @escaping (Element, Element) -> Bool) -> Bool {
    var localCopy = array

    let countOfUncommonElements = reduce(0) { (uncommonElementsCount: Int, item: Element) -> Int in
      if let index = localCopy.index(where: {condition(item, $0)}) {
        localCopy.remove(at: index)
        return uncommonElementsCount
      } else {
        return uncommonElementsCount + 1
      }
    }

    return countOfUncommonElements == 0 && count == array.count
  }
}

func enumComparisonClosure<T: RawRepresentable>(firstItem: T, secondItem: T) -> Bool where T.RawValue: Equatable {
  return firstItem == secondItem
}

How I'm using it at the moment:

class Somewhere {
  enum EnumType: String {
    case first
    case second
  }

  func method() {
    let firstArray: [EnumType] = [.first, .second]
    let secondArray: [EnumType] = [.second]

    firstArray.containsTheSameElements(as: secondArray, condition: enumComparisonClosure)
  }
}

How I would like to use it:

firstArray.containsTheSameElements(as: secondArray)

How I would like to be able to implement it:

public extension Array {
  func containsTheSameElements<Element: RawRepresentable>(as array: [Element]) -> Bool where Element.RawValue: Equatable {
    return containsTheSameElements(as: array, condition: enumComparisonClosure)
  }
}

How can I constrain the Array's extension "Element" typealias to be a RawRepresentable with RawValue of Equatable type?

Or what would be another approach to make this comparison possible?


Solution

  • The constraints on the extension are separated by a comma, not by &. The containsTheSameElements must take a [T] argument where T is a RawRepresentable with the same RawValue as the array elements.

    Example:

    extension Array where Element: RawRepresentable, Element.RawValue: Equatable {
    
        func containsTheSameElements<T>(as array: [T]) -> Bool
        where T: RawRepresentable, T.RawValue == Element.RawValue
        {
            return self.count == array.count &&
                !zip(self, array).contains { $0.rawValue != $1.rawValue }
        }
    }
    

    Update: It is simpler if you need it only for arrays of the same type:

    extension Array where Element: RawRepresentable, Element.RawValue: Equatable {
    
        func containsTheSameElements(as array: [Element]) -> Bool
        {
            return self.count == array.count &&
                !zip(self, array).contains { $0.rawValue != $1.rawValue }
        }
    }
    

    or, reusing your existing method:

    extension Array where Element: RawRepresentable, Element.RawValue: Equatable {
    
        func containsTheSameElements(as array: [Element]) -> Bool
        {
            return containsTheSameElements(as: array, condition: enumComparisonClosure)
        }
    }