Skip to content

Instantly share code, notes, and snippets.

@jpmhouston
Last active April 19, 2024 18:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpmhouston/72eb8e6681b7cc2499ca1c350b5f0c09 to your computer and use it in GitHub Desktop.
Save jpmhouston/72eb8e6681b7cc2499ca1c350b5f0c09 to your computer and use it in GitHub Desktop.
Swift methods implementing the inverse of contains()
// Insired by https://forums.swift.org/t/iscontained-in-array-extension-of-equatable/20223/28
// Exercise for the reader: define operators ∈ and ∉
//
// Created primarily for excluded, which I wanted to use in an
// expression like:
// `let foos = things.compactMap({ $0.foo?.excluded(from: [a,b,c]) })`
// I then went on to generalize to included(in:) and all the
// variations below, and found an even better way to write similar
// expressions: `let x = y.excluding([u,v,w])`
//
// Note, I had to add the subtle variant of these functions when
// a,b,c are optionals of foo's type so that it wouldn't fail with
// "Cannot convert value of type 'Foo?' to expected element type 'Foo'".
// Before adding those variants I instead needed an expression like:
// `compactMap({ $0.foo?.excludedFrom([a,b,c].compactMap({$0})) })`
//
// I then added inverse of Range.contains(), which I thought was best
// named like inside(range r:), outside(range r:)
//
public extension Equatable {
func isIncluded<S: Sequence>(in s: S) -> Bool where S.Element == Self {
s.contains(self)
}
func isIncluded<S: Sequence>(in s: S) -> Bool where S.Element == Self? {
s.contains(self)
}
func isIncluded(in s: Self...) -> Bool {
s.contains(self)
}
func isIncluded(in s: Self?...) -> Bool {
s.contains(self)
}
func isExcluded<S: Sequence>(in s: S) -> Bool where S.Element == Self {
!s.contains(self)
}
func isExcluded<S: Sequence>(in s: S) -> Bool where S.Element == Self? {
!s.contains(self)
}
func isExcluded(in s: Self...) -> Bool {
!s.contains(self)
}
func isExcluded(in s: Self?...) -> Bool {
!s.contains(self)
}
func included<S: Sequence>(in s: S) -> Self? where S.Element == Self {
s.contains(self) ? self : nil
}
func included<S: Sequence>(in s: S) -> Self? where S.Element == Self? {
s.contains(self) ? self : nil
}
func included(in s: Self...) -> Self? {
s.contains(self) ? self : nil
}
func included(in s: Self?...) -> Self? {
s.contains(self) ? self : nil
}
func excluded<S: Sequence>(from s: S) -> Self? where S.Element == Self {
s.contains(self) ? nil : self
}
func excluded<S: Sequence>(from s: S) -> Self? where S.Element == Self? {
s.contains(self) ? nil : self
}
func excluded(from s: Self...) -> Self? {
s.contains(self) ? nil : self
}
func excluded(from s: Self?...) -> Self? {
s.contains(self) ? nil : self
}
}
// oh, maybe these are just other names for intersection, subtract :shrug:
// maybe find another variation that better describes which is iterated over
// and which is more optimal to call contains on, self or the parameter
public extension Sequence where Element: Equatable {
func includes<S: Sequence>(_ s: S) -> Bool where S.Element == Element {
contains { s.contains($0) }
}
func includes<S: Sequence>(_ s: S) -> Bool where S.Element == Element? {
contains { s.contains($0) }
}
func includes(_ s: Element...) -> Bool {
contains { s.contains($0) }
}
func includes(_ s: Element?...) -> Bool {
contains { s.contains($0) }
}
func excludes<S: Sequence>(_ s: S) -> Bool where S.Element == Element {
contains { !s.contains($0) }
}
func excludes<S: Sequence>(_ s: S) -> Bool where S.Element == Element? {
contains { !s.contains($0) }
}
func excludes(_ s: Element...) -> Bool {
contains { !s.contains($0) }
}
func excludes(_ s: Element?...) -> Bool {
contains { !s.contains($0) }
}
func including<S: Sequence>(_ s: S) -> some Sequence<Element> where S.Element == Element {
filter { s.contains($0) }
}
func including<S: Sequence>(_ s: S) -> some Sequence<Element> where S.Element == Element? {
filter { s.contains($0) }
}
func including(_ s: Element...) -> some Sequence<Element> {
filter { s.contains($0) }
}
func including(_ s: Element?...) -> some Sequence<Element> {
filter { s.contains($0) }
}
func excluding<S: Sequence>(_ s: S) -> some Sequence<Element> where S.Element == Element {
filter { !s.contains($0) }
}
func excluding<S: Sequence>(_ s: S) -> some Sequence<Element> where S.Element == Element? {
filter { !s.contains($0) }
}
func excluding(_ s: Element...) -> some Sequence<Element> {
filter { !s.contains($0) }
}
func excluding(_ s: Element?...) -> some Sequence<Element> {
filter { !s.contains($0) }
}
}
public extension Comparable {
func isInside(range r: Range<Self>) -> Bool {
r.contains(self)
}
func isInside(range r: ClosedRange<Self>) -> Bool {
r.contains(self)
}
func isOutside(range r: Range<Self>) -> Bool {
!r.contains(self)
}
func isOutside(range r: ClosedRange<Self>) -> Bool {
!r.contains(self)
}
func inside(range r: Range<Self>) -> Self? {
r.contains(self) ? self : nil
}
func inside(range r: ClosedRange<Self>) -> Self? {
r.contains(self) ? self : nil
}
func outside(range r: Range<Self>) -> Self? {
r.contains(self) ? nil : self
}
func outside(range r: ClosedRange<Self>) -> Self? {
r.contains(self) ? nil : self
}
}
public extension Sequence where Element: Comparable {
func thoseInside<S: Sequence>(range r: Range<Element>) -> some Sequence<Element> where S.Element == Element {
filter { r.contains($0) }
}
func thoseInside<S: Sequence>(range r: ClosedRange<Element>) -> some Sequence<Element> where S.Element == Element {
filter { r.contains($0) }
}
func thoseOutside<S: Sequence>(range r: Range<Element>) -> some Sequence<Element> where S.Element == Element {
filter { !r.contains($0) }
}
func thoseOutside<S: Sequence>(range r: ClosedRange<Element>) -> some Sequence<Element> where S.Element == Element {
filter { !r.contains($0) }
}
}
/*
let evenDigits = [0,2,4,6,8]
let oddDigits = [1,3,5,7,9]
let evenDigitsSet = Set([0,2,4,6,8])
let oddDigitsSet = Set([1,3,5,7,9])
print("1 is even: \(1.isIncludedIn(evenDigits))") // 1 is even: false
print("2 is even: \(2.isIncludedIn(evenDigitsSet))") // 2 is even: true
print("3 is odd: \(3.isIncludedIn(1,3,5,7,9))") // 3 is odd: true
print("4 is odd: \(4.isIncludedIn(1,3,5,7,9))") // 4 is odd: false
print("even: \([1,2,3,4].compactMap { $0.includedIn(evenDigits) })") // even: [2, 4]
print("odd: \([1,2,3,4].compactMap { $0.excludedFrom(evenDigitsSet) })") // odd: [1, 3]
print("even: \([3,4,5,6].excluding(oddDigitsSet))") // even: [4, 6]
print("odd: \([3,4,5,6].including(oddDigits))") // odd: [3, 5]
print("even: \([6,7,8,9].including(0,2,4,6,8))") // even: [6, 8]
print("odd: \([6,7,8,9].excluding(0,2,4,6,8))") // odd: [7, 9]
(hmm those don't exercise the overloads with optionals)
*/
/*
i named them like this originally:
file Equatable+IsIn.swift
public extension Equatable {
func isIn<S: Sequence>(_ s: S) -> Bool where S.Element == Self {
s.contains(self)
}
func isNotIn<S: Sequence>(_ s: S) -> Bool where S.Element == Self {
!s.contains(self)
}
func onlyIfIn<S: Sequence>(_ s: S) -> Self? where S.Element == Self {
s.contains(self) ? self : nil
}
func onlyIfNotIn<S: Sequence>(_ s: S) -> Self? where S.Element == Self { // got suggestion to rename this unlessIn
s.contains(self) ? nil : self
}
func isIn(_ s: Self...) -> Bool {
s.contains(self)
}
func isNotIn(_ s: Self...) -> Bool {
!s.contains(self)
}
func onlyIfIn(_ s: Self...) -> Self? {
s.contains(self) ? self : nil
}
func onlyIfNotIn(_ s: Self...) -> Self? { // got suggestion to rename this unlessIn
s.contains(self) ? nil : self
}
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment