Home > database >  How can I chain functions when i call it in Swift?
How can I chain functions when i call it in Swift?

Time:02-10

I am beginner in swift and I'm doing exercises to learn.

I have to filter any numbers that are even, sort the array, map them to string and print the result one item per line.

The problem is, when I call the function, I´m not able to chain the closures, and it only prints the third function.

import Foundation

let luckyNumbers = [7, 4, 38, 21, 16, 15, 12, 33, 31, 49]

func doImportantWorkChain (first: ([Int]) -> [Int], second: ([Int]) -> [Int], third: ([Int]) -> [String]) {
    first(luckyNumbers)
    second(luckyNumbers)
    third(luckyNumbers)

    third(luckyNumbers).forEach {
        print($0)
    }
}

doImportantWorkChain { number in
    return number.filter {
        return $0.isMultiple(of: 2)
    }
} second: { number in
    return number.sorted {
        return $0 < $1
    }
} third: { number in
    return number.map {
       ("\($0) is the lucky number")
    }
}

Output

7 is the lucky number
4 is the lucky number
38 is the lucky number
21 is the lucky number
16 is the lucky number
15 is the lucky number
12 is the lucky number
33 is the lucky number
31 is the lucky number
49 is the lucky number

Output I have to get

4 is the lucky number
12 is the lucky number
16 is the lucky number
38 is the lucky number

I know that I have to use: luckyNumbers.first { }.second { }

When I try to write like this Xcode give me error: value of type '(([Int]) -> [Int], ([Int]) -> [Int], ([Int]) -> [String]) -> ()' has no member 'first'

So I erase first and give me this errors:

Expected member name following '.'

Top-level statement cannot begin with a closure expression

Value of type '(_) -> _' has no member 'second'

doImportantWorkChain.first{ number in
    return number.filter {
        return $0.isMultiple(of: 2)
    }
}.second{ number in
    return number.sorted {
        return $0 < $1
    }
}.third{ number in
    return number.map {
       ("\($0) is the lucky number")
    }
}

Also I try to chain it when when I call them inside the function but doesn't work either.

CodePudding user response:

You are not using results from previous functions to next.

If you do so then you will end up with error on this third(luckyNumbers).forEach line as function expected [Int] and you will be going to pass [String] as output from this third(luckyNumbers) call.

To work this code, you can have following modifications and run

 let luckyNumbers = [7, 4, 38, 21, 16, 15, 12, 33, 31, 49]

 func doImportantWorkChain (first: ([Int]) -> [Int], second: ([Int]) -> 
    [Int], third: ([Int]) -> [String]) {
    let first_result = first(luckyNumbers)
    let second_result = second(first_result)
    let third_result = third(second_result)

    for result in third_result {
      print(result)
    }
 }

doImportantWorkChain { number in
  return number.filter {
      return $0.isMultiple(of: 2)
  }
} second: { number in
  return number.sorted {
      return $0 < $1
  }
} third: { number in
  return number.map {
     ("\($0) is the lucky number")
  }
}

CodePudding user response:

As the first answer suggested, the issue is that the result of a closure should be used in the next closure.

The fastest way to get the output you are looking for

func doImportantWorkChain(first: ([Int]) -> [Int],
                          second: ([Int]) -> [Int],
                          third: ([Int]) -> [String]) {

    third(second(first(luckyNumbers))).forEach {
        print($0)
    }
}

However, this is not really chaining functions together and it is doing what the first answer with and just reduces your lines of code but there is no real upgrade in this.

The right way (IMO) to get the output you are looking for

Chaining would be something that I believe one of the commenters suggested is using the filter, sort and map functions directly on luckyNumbers to get your desired output:

luckyNumbers
    .filter { $0.isMultiple(of: 2) }
    .sorted() { return $0 < $1 }
    .map { ("\($0) is the lucky number") }
    .forEach { print($0) }

A way that is chaining, will give you the desired output but I don't know why you would ever use this

If you think about what you need to do, to achieve some sort of chaining, you need to create your own higher order functions like filter, map etc with the same signatures as your closures and then executing the closures.

I did this by creating an extension for Int arrays

extension Array where Element == Int
{
    func apply(_ operation: ([Int]) -> [Int]) -> [Int]
    {
        return operation(self)
    }
    
    func apply(_ operation: ([Int]) -> [String]) -> [String]
    {
        return operation(self)
    }
}

Then you can achieve chaining and get your desired result

func doImportantWorkChain(first: ([Int]) -> [Int],
                          second: ([Int]) -> [Int],
                          third: ([Int]) -> [String]) {
    
    luckyNumbers
        .apply(first)
        .apply(second)
        .apply(third)
        .forEach { print($0) }
}

Again, this might be a nice exercise to work your brain and explore the language and its capabilities but I don't think you will ever really use this.

All three of the above ways will give you this output:

4 is the lucky number
12 is the lucky number
16 is the lucky number
38 is the lucky number
  • Related