Skip to content

Commit 3430f38

Browse files
authored
Merge pull request #11576 from airspeedswift/remove-elements
2 parents 43c54a6 + ba11a23 commit 3430f38

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

stdlib/public/core/RangeReplaceableCollection.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,15 @@ public protocol RangeReplaceableCollection : Collection
353353
/// - Complexity: O(*n*), where *n* is the length of the collection.
354354
mutating func removeAll(keepingCapacity keepCapacity: Bool /*= false*/)
355355

356+
/// Removes from the collection all elements that satisfy the given predicate.
357+
///
358+
/// - Parameter predicate: A closure that takes an element of the
359+
/// sequence as its argument and returns a Boolean value indicating
360+
/// whether the element should be removed from the collection.
361+
///
362+
/// - Complexity: O(*n*), where *n* is the length of the collection.
363+
mutating func removeAll(where predicate: (Element) throws -> Bool) rethrows
364+
356365
// FIXME(ABI): Associated type inference requires this.
357366
subscript(bounds: Index) -> Element { get }
358367

@@ -1075,3 +1084,47 @@ extension RangeReplaceableCollection {
10751084
return try Self(self.lazy.filter(isIncluded))
10761085
}
10771086
}
1087+
1088+
extension RangeReplaceableCollection where Self: MutableCollection {
1089+
/// Removes from the collection all elements that satisfy the given predicate.
1090+
///
1091+
/// - Parameter predicate: A closure that takes an element of the
1092+
/// sequence as its argument and returns a Boolean value indicating
1093+
/// whether the element should be removed from the collection.
1094+
///
1095+
/// - Complexity: O(*n*), where *n* is the length of the collection.
1096+
@_inlineable
1097+
public mutating func removeAll(
1098+
where predicate: (Element) throws -> Bool
1099+
) rethrows {
1100+
if var i = try index(where: predicate) {
1101+
var j = index(after: i)
1102+
while j != endIndex {
1103+
if try !predicate(self[j]) {
1104+
swapAt(i, j)
1105+
formIndex(after: &i)
1106+
}
1107+
formIndex(after: &j)
1108+
}
1109+
removeSubrange(i...)
1110+
}
1111+
}
1112+
}
1113+
1114+
extension RangeReplaceableCollection {
1115+
/// Removes from the collection all elements that satisfy the given predicate.
1116+
///
1117+
/// - Parameter predicate: A closure that takes an element of the
1118+
/// sequence as its argument and returns a Boolean value indicating
1119+
/// whether the element should be removed from the collection.
1120+
///
1121+
/// - Complexity: O(*n*), where *n* is the length of the collection.
1122+
@_inlineable
1123+
public mutating func removeAll(
1124+
where predicate: (Element) throws -> Bool
1125+
) rethrows {
1126+
// FIXME: Switch to using RRC.filter once stdlib is compiled for 4.0
1127+
// self = try filter { try !predicate($0) }
1128+
self = try Self(self.lazy.filter { try !predicate($0) })
1129+
}
1130+
}

test/stdlib/RemoveElements.swift

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//===--- RemoveElements.swift - tests for lazy filtering-------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
// RUN: %target-run-simple-swift
13+
// REQUIRES: executable_test
14+
15+
import StdlibUnittest
16+
17+
18+
let RemoveElements = TestSuite("RemoveElements")
19+
20+
extension RangeReplaceableCollection where Element: Equatable {
21+
mutating func remove(equalTo: Element) {
22+
removeAll(where: { $0 == equalTo })
23+
}
24+
}
25+
26+
RemoveElements.test("removing-array-with-predicate") {
27+
var a = Array(0..<10)
28+
a.removeAll(where: { $0 % 2 == 0 })
29+
expectEqualSequence([1,3,5,7,9], a)
30+
}
31+
32+
RemoveElements.test("removing-array-nothing") {
33+
var a = Array(0..<5)
34+
a.removeAll(where: { _ in false })
35+
expectEqualSequence(0..<5, a)
36+
}
37+
38+
RemoveElements.test("removing-array-everything") {
39+
var a = Array(0..<5)
40+
a.removeAll(where: { _ in true })
41+
expectEqualSequence([], a)
42+
}
43+
44+
RemoveElements.test("removing-array-from-empty") {
45+
var a: [Int] = []
46+
a.removeAll(where: { _ in true })
47+
expectEqualSequence([], a)
48+
}
49+
50+
RemoveElements.test("removing-array-with-equatable") {
51+
var a = Array(0..<5)
52+
a.remove(equalTo: 6)
53+
expectEqualSequence([0,1,2,3,4], a)
54+
a.remove(equalTo: 3)
55+
expectEqualSequence([0,1,2,4], a)
56+
a.remove(equalTo: 0)
57+
expectEqualSequence([1,2,4], a)
58+
a.remove(equalTo: 4)
59+
expectEqualSequence([1,2], a)
60+
a.remove(equalTo: 1)
61+
expectEqualSequence([2], a)
62+
a.remove(equalTo: 2)
63+
expectEqualSequence([], a)
64+
}
65+
66+
RemoveElements.test("removing-string-with-predicate") {
67+
var s = "0123456789"
68+
s.removeAll(where: { Int(String($0))! % 2 == 0 })
69+
expectEqualSequence("13579", s)
70+
}
71+
72+
RemoveElements.test("removing-string-nothing") {
73+
var s = "01234"
74+
s.removeAll(where: { _ in false })
75+
expectEqualSequence("01234", s)
76+
}
77+
78+
RemoveElements.test("removing-string-everything") {
79+
var s = "01234"
80+
s.removeAll(where: { _ in true })
81+
expectEqualSequence("", s)
82+
}
83+
84+
RemoveElements.test("removing-string-from-empty") {
85+
var s = ""
86+
s.removeAll(where: { _ in true })
87+
expectEqualSequence("", s)
88+
}
89+
90+
RemoveElements.test("removing-string-with-equatable") {
91+
var s = "01234"
92+
s.remove(equalTo: "6")
93+
expectEqualSequence("01234", s)
94+
s.remove(equalTo: "3")
95+
expectEqualSequence("0124", s)
96+
s.remove(equalTo: "0")
97+
expectEqualSequence("124", s)
98+
s.remove(equalTo: "4")
99+
expectEqualSequence("12", s)
100+
s.remove(equalTo: "1")
101+
expectEqualSequence("2", s)
102+
s.remove(equalTo: "2")
103+
expectEqualSequence("", s)
104+
}
105+
106+
runAllTests()
107+

0 commit comments

Comments
 (0)