Skip to content

Commit

Permalink
Add BidirectionalCollection.trimmed
Browse files Browse the repository at this point in the history
  • Loading branch information
karwa committed Oct 13, 2020
1 parent 7ab0a20 commit 60fcfb9
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 0 deletions.
48 changes: 48 additions & 0 deletions Sources/Algorithms/Trim.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Algorithms open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

extension BidirectionalCollection {

/// Returns a `SubSequence` formed by discarding all elements at the start and end of the Collection
/// which satisfy the given predicate.
///
/// Trimming is a common operation in computing, often referring to removing leading and trailing whitespace
/// from a string[1].
///
/// ```
/// let myString = " hello, world "
/// print(myString.trimmed(where: \.isWhitespace)) // "hello, world"
/// ```
///
/// [1]: https://en.wikipedia.org/wiki/Trimming_(computer_programming)
///
/// - parameters:
/// - predicate: A closure which determines if the element should be omitted from the resulting slice.
///
/// - complexity: `O(n)`, where `n` is the length of the Collection.
///
@inlinable
public func trimmed(where predicate: (Element) throws -> Bool) rethrows -> SubSequence {

// Consume elements from the front.
let sliceStart = try firstIndex(where: { try predicate($0) == false }) ?? endIndex
// sliceEnd is the index _after_ the last index to match the predicate.
var sliceEnd = endIndex
while sliceStart != sliceEnd {
let idxBeforeSliceEnd = index(before: sliceEnd)
guard try predicate(self[idxBeforeSliceEnd]) else {
return self[sliceStart..<sliceEnd]
}
sliceEnd = idxBeforeSliceEnd
}
return self[Range(uncheckedBounds: (sliceStart, sliceStart))] // Trimmed everything.
}
}
57 changes: 57 additions & 0 deletions Tests/SwiftAlgorithmsTests/TrimTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Algorithms open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

import XCTest
import Algorithms

final class TrimTests: XCTestCase {

func testEmpty() {
let results_empty = ([] as [Int]).trimmed { $0.isMultiple(of: 2) }
XCTAssertEqual(results_empty, [])
}

func testNoMatch() {
// No match (nothing trimmed).
let results_nomatch = [1, 3, 5, 7, 9, 11, 13, 15].trimmed { $0.isMultiple(of: 2) }
XCTAssertEqual(results_nomatch, [1, 3, 5, 7, 9, 11, 13, 15])
}

func testNoTailMatch() {
// No tail match (only trim head).
let results_notailmatch = [1, 3, 5, 7, 9, 11, 13, 15].trimmed { $0 < 10 }
XCTAssertEqual(results_notailmatch, [11, 13, 15])
}

func testNoHeadMatch() {
// No head match (only trim tail).
let results_noheadmatch = [1, 3, 5, 7, 9, 11, 13, 15].trimmed { $0 > 10 }
XCTAssertEqual(results_noheadmatch, [1, 3, 5, 7, 9])
}

func testBothEndsMatch() {
// Both ends match, some string of >1 elements do not match (return that string).
let results = [2, 10, 11, 15, 20, 21, 100].trimmed(where: { $0.isMultiple(of: 2) })
XCTAssertEqual(results, [11, 15, 20, 21])
}

func testEverythingMatches() {
// Everything matches (trim everything).
let results_allmatch = [1, 3, 5, 7, 9, 11, 13, 15].trimmed { _ in true }
XCTAssertEqual(results_allmatch, [])
}

func testEverythingButOneMatches() {
// Both ends match, one element does not match (trim everything except that element).
let results_onematch = [2, 10, 12, 15, 20, 100].trimmed { $0.isMultiple(of: 2) }
XCTAssertEqual(results_onematch, [15])
}
}

0 comments on commit 60fcfb9

Please sign in to comment.