4

I'm trying to build a function with swift that will map an array, divide each value in the array by 3, then spit out a new array. This is what I have so far:

    func divideby3Map<T, U>(y: [T], z: T -> U) -> [U] {

        let array = [int]()
        let divideby3Array = array.map { [y] / 3 }

        return dividedby3Array
    }

    divideby3Map([1,2,3,4,5])

Where T and U are the original array, and the new array being returned respectively, and it's done using generics.

I'm sure this isn't written properly, I'm stuck in terms of the right syntax. For example, since the array being returned is represented by the generic [U], I assume I have to use it somewhere in the array being returned, not sure where though.

1
  • If it is only math why don't you just use Int or Double? Commented Apr 21, 2015 at 21:59

1 Answer 1

7

When writing a generic function, it’s sometimes easier to approach it in 3 steps: first write the code stand-alone using a specific type. Then write the code as a function, still with a specific type. Finally, change the function to be generic.

The first part, dividing an array by 3, can be done like this:

let a = [1,2,3,4,5]
// map is run on the array of integers, and returns a new
// array with the operation performed on each element in a:
let b = a.map { $0 / 3 }
// so b will be [0,0,1,1,1]
// (don’t forget, integer division truncates)

Note the closure you provide between the { } is an operation that will be applied to each element of the array. $0 represents the element, and you divide it by 3. You could also write it as a.map { i in i / 3 }.

To put this into its own function:

func divideby3Map(source: [Int]) -> [Int] {
    return source.map { $0 / 3 }
}

No need to declare a fresh array – map will create one for you. You can then return that directly (you can assign it to a temporary if you prefer, but that isn’t really necessary).

Finally, if you want to make it generic, start by adding a placeholder:

func divideby3Map<T>(source: [T]) -> [T] {
    return source.map { $0 / 3 }
}

Note, there’s only a need for one placeholder, T, because you are returning the exact same type you are passed in.

Except… this won’t compile, because the compiler doesn’t know that T is guaranteed to provide two critical things: the ability to divide (a / operator), and the ability to create new T from integer literals (i.e. to create a T with value 3 to divide by). Otherwise, what if we passed an array of strings or an array of arrays in?

To do this, we need to “constrain” T so our function will only accept as arguments types that provide these features. One such protocol we can use to constrain T is IntegerType, which does guarantee these features (as well as some other ones like +, * etc):

func divideby3Map<T: IntegerType>(source: [T]) -> [T] {
    return source.map { $0 / 3 }
}

divideby3Map(a) // returns [0,0,1,1,1]

let smallInts: [UInt8] = [3,6,9]
divideby3Map(smallInts)  // returns [1,2,3]
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.