0

I've been playing around with Generics and Extensions to existing types in Swift 3. I wrote two generic Array functions that extends Array with find-and-replace methods, named replaced() and replace(). The replaced() function works as intended but the replace() function has a compile time error. Here is the code and a test of one of the methods.

extension Array {
    func replaced<T: Equatable>(each valueToReplace: T, with newValue: T) -> [T] {
        var newArray:[T] = []
        for index:Int in 0..<self.count {
            if let temp = self[index] as? T, temp == valueToReplace{
                newArray.append(newValue)
            }else{
                newArray.append(self[index] as! T)
            }
        }
        return newArray
    }
    mutating func replace<T: Equatable>(each valueToReplace: T, with newValue: T) {
        for index:Int in 0..<self.count {
            if let temp = self[index] as? T, temp == valueToReplace {
                // FIXME: self[index] = newValue 
            }
        }
        return
    }
}
var j = [1,2,3,4,3,6,3,8,9]
var newArray = j.replaced(each: 3, with: 0)

I get a compile time error on the second method, replace(), at the line commented out with "//FIXME:" annotation. The compile time error says, "Ambiguous reference to member 'subscript'".

How can I fix the replace() code so it works?

Hamish
  • 78,605
  • 19
  • 187
  • 280
RMH
  • 801
  • 2
  • 8
  • 17
  • 1
    Very similar problem to http://stackoverflow.com/q/41045212/2976878 – you're defining a new local generic placeholder `T` instead of using the `Array`'s `Element` type. – Hamish Mar 09 '17 at 22:52
  • just make the constraint at the array extension `extension Array where Element: Equatable {` – Leo Dabus Mar 09 '17 at 22:53
  • Once you're using `Element`, you can simply your implementation of `replaced` significantly to `return map { $0 == valueToReplace ? newValue : $0 }` btw :) – Hamish Mar 09 '17 at 22:55

1 Answers1

1

Give this a shot

extension Array where Element: Equatable {
    func replaced (each valueToReplace: Element, with newValue: Element) -> [Element] {
        var newArray = [Element]()
        newArray.reserveCapacity(self.count)

        for element in self {
            let newElement = (element == valueToReplace) ? newValue : element
            newArray.append(newElement) 
        }

        return newArray
    }

    mutating func replace(each valueToReplace: Element, with newValue: Element) {
        for (i, element) in self.enumerated() {
            if element == valueToReplace { self[i] = newValue }
        }
    }
}

var j = [1,2,3,4,3,6,3,8,9]
var newArray = j.replaced(each: 3, with: 0)

It would be better to remove the redundancy by just making replaced delegate to replace:

extension Array where Element: Equatable {
    func replaced(each valueToReplace: Element, with newValue: Element) -> [Element] {
        var copy = self
        copy.replace(each: valueToReplace, with: newValue)
        return copy
    }

    mutating func replace(each valueToReplace: Element, with newValue: Element) {
        for (i, element) in self.enumerated() {
            if element == valueToReplace { self[i] = newValue }
        }
    }
}
Alexander
  • 59,041
  • 12
  • 98
  • 151
  • `mutating func replace(each element: Element, with newElement: Element) { self = map { $0 == element ? newElement : $0 } }` – Leo Dabus Mar 09 '17 at 22:58
  • 1
    @Alexander For `replace` you could use a `where` clause on the for loop – `for (index, element) in self.enumerated() where element == valueToReplace { self[index] = newValue }` :) – Hamish Mar 09 '17 at 23:01
  • @Hamish I considered that, but it's make the line long and it's more clutter than it's worth, imo. I would prefer a where clause when theres a large if block that's heavily indented, but that isn't the case here – Alexander Mar 09 '17 at 23:04
  • @Alexander Thanks for the clarification. Much appreciated!! Also thanks to LeoDabus, Hamish. You guys rock! – RMH Mar 10 '17 at 00:03