🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of fmortens

fmortens's solution

to Accumulate in the Swift Track

Published at Mar 22 2021 · 1 comment
Instructions
Test suite
Solution

Implement the accumulate operation, which, given a collection and an operation to perform on each element of the collection, returns a new collection containing the result of applying that operation to each element of the input collection.

Given the collection of numbers:

  • 1, 2, 3, 4, 5

And the operation:

  • square a number (x => x * x)

Your code should be able to produce the collection of squares:

  • 1, 4, 9, 16, 25

Check out the test suite to see the expected function signature.

Restrictions

Keep your hands off that collect/map/fmap/whatchamacallit functionality provided by your standard library! Solve this one yourself using other basic tools instead.

Setup

Go through the project setup instructions for Xcode using Swift:

http://exercism.io/languages/swift
http://exercism.io/languages/swift/tests

Notably from the source directory:

swift test runs tests
swift package generate-xcodeproj creates an Xcode project

Source

Conversation with James Edward Gray II https://twitter.com/jeg2

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

AccumulateTests.swift

import XCTest
@testable import Accumulate

private extension String {
    var length: Int { return self.count }

    func reverse() -> String {
        var result: String = ""
        for char in self {
            result = "\(char)\(result)"
        }
        return result
    }
}

private extension XCTest {
    func XCTAssertEqualMultiArray(_ aArray1: [[String]], _ aArray2: [[String]]) {
        XCTAssertEqual(Array(aArray1.joined()), Array(aArray2.joined()))
    }
}

class AccumulateTests: XCTestCase {
    func testEmptyAccumulation() {
        let input = [Int]()
        func square(_ input: Int) -> Int {
            return input * input
        }
        let result = input.accumulate(square)

        XCTAssertTrue(result.isEmpty)
    }

    func testAccumulateSquares() {
        let input = [1, 2, 3, 4]
        let expected = [1, 4, 9, 16]
        func square(_ input: Int) -> Int {
            return input * input
        }
        let result = input.accumulate(square)

        XCTAssertEqual(expected, result)
    }

    func testAccumulateUpcases() {
        let input = ["hello", "world"]
        let expected = ["HELLO", "WORLD"]
        func toUpper(_ input: String) -> String {
            return input.uppercased()
        }
        let result = input.accumulate(toUpper)

        XCTAssertEqual(expected, result)

    }

    func testAccumulateReversedStrings() {
        let input =    ["the", "quick", "brown", "fox", "etc"]
        let expected = ["eht", "kciuq", "nworb", "xof", "cte"]
        func reverse(_ input: String) -> String {
            return input.reverse()
        }
        let result = input.accumulate(reverse)

        XCTAssertEqual(expected, result)
    }

    func testAccumulateRecursively() {
        let input =   ["a", "b", "c"]
        let expected = [
            ["a1", "a2", "a3"],
            ["b1", "b2", "b3"],
            ["c1", "c2", "c3"]
        ]
        func recurse(_ input: String) -> [String] {
            func appendTo(_ innerInput: String) -> String {
                return input+innerInput
            }
            let result = ["1", "2", "3"].accumulate(appendTo)
            return result
        }
        let result = input.accumulate(recurse)

        XCTAssertEqualMultiArray(expected, result)
    }

    static var allTests: [(String, (AccumulateTests) -> () throws -> Void)] {
        return [
            ("testEmptyAccumulation", testEmptyAccumulation),
            ("testAccumulateSquares", testAccumulateSquares),
            ("testAccumulateUpcases", testAccumulateUpcases),
            ("testAccumulateReversedStrings", testAccumulateReversedStrings),
            ("testAccumulateRecursively", testAccumulateRecursively),
        ]
    }
}

LinuxMain.swift

import XCTest
@testable import AccumulateTests

XCTMain([
    testCase(AccumulateTests.allTests),
    ])
extension Array {
    
    func accumulate<T>(_ callback: (Element) -> T) -> [T] {
        self.map { callback($0) }
    }

}

Community comments

Find this solution interesting? Ask the author a question to learn more.
Avatar of fmortens
fmortens
Solution Author
commented 94 days ago

For this I'm using self to indicate that for each element we call the callback with the current value

fmortens's Reflection

Ok, so this took me for ages. I simply didn't "get it" and saw I was to supply the method with an operation/function and I went in circles.