A μframework of extensions for SequenceType in Swift 2.0, inspired by Python's itertools, Haskell's standard library, and other things.

Overview

Build Status Carthage compatible

SwiftSequence

Full reference here.

(If you're looking for data structures in Swift, those have been moved to here)

SwiftSequence is a lightweight framework of extensions to SequenceType. It has no requirements beyond the Swift standard library. Every function and method has both a strict and a lazy version, unless otherwise specified.

To make an eager sequence lazy, the lazy property can be used:

[1, 2, 3].lazy.hop(2)

// 1, 3

Contributing

Contributions welcome! Anything at all, as long as it's related to sequences, Linux-compatible, and has a test or two.

Contents

  • [ScanReduce] (#scanreduce)
  • [TakeDrop] (#takedrop)
  • [Hopping and Slicing] (#hopping-and-slicing)
  • [Interpose] (#interpose)
  • [Combinations] (#combinations)
  • [Permutations] (#permutations)
  • [Cycle] (#cycle)
  • [Categorise] (#categorise)
  • [ChunkWindowSplit] (#chunkwindowsplit)
  • [Enumerate] (#enumerate)
  • [Finding] (#finding)
  • [NestedSequences] (#nestedsequences)
  • [Zip] (#zip)

ScanReduce

Reduce

func reduce
    (@noescape combine: (accumulator: Generator.Element, element: Generator.Element) throws -> Generator.Element)
    rethrows -> Generator.Element?

This function works the same as the standard library reduce, except it takes the initial value to be the first element of self. It returns an optional, which is nil if self is empty.

[1, 2, 3, 4].reduce(+) == 10

Scan

Returns an array of the intermediate results of calling combine on successive elements of self and an accumulator:

[1, 2, 3].scan(0, combine: +)
 
[1, 3, 6]

The last element of this array will be equal to the result of calling reduce with the same arguments.

There is also, like reduce, a version that takes the first element of the sequence to be initial:

[1, 2, 3, 4, 5].scan(+)

[3, 6, 10, 15]

This also is evaluated lazily if the sequence it is called on is lazy.

TakeDrop

prefixWhile

This function returns all of the elements of self up until an element returns false for predicate:

[1, 2, 3, 4, 5, 2].prefixWhile { $0 < 4 }

[1, 2, 3]

Note that it's not the same as filter: if any elements return true for the predicate after the first element that returns false, they're still not returned.

dropWhile

Similar in behaviour to prefixWhile, this function drops the first elements of self that return true for a predicate:

lazy([1, 2, 3, 4, 5, 2]).dropWhile { $0 < 4 }

4, 5, 2

breakAt

Returns a tuple, the first element being the prefix of self of maximum length n, the second being the remaining elements of self.

[1, 2, 3, 4].breakAt(3) == ([1, 2, 3], [4])

This also has a version which takes a predicate, which returns a tuple where the first element is the prefix of self up until the first element that returns true for isBreak.

Hopping and Slicing

This allows subscripting of several CollectionTypes in a way similar to Python's slicing. For instance, you can slice in an open-ended way:

let array = [0, 1, 2, 3, 4, 5, 6]
array[3...] == [3, 4, 5, 6]
array[...4] == [0, 1, 2, 3, 4]
array[..<4] == [0, 1, 2, 3]

You can also mutate via slices:

var array = [0, 1, 2, 3, 4, 5, 6]
array[...3] = [10, 11, 12, 13]
array == [10, 11, 12, 13, 4, 5, 6]

Or, you can hop over elements in the array:

let array = [0, 1, 2, 3, 4, 5, 6]
array[2..., by: 2] // [2, 4, 6]

Interpose

These functions allow lazy and eager insertion of elements into sequences at regular intervals.

Interpose

This function returns self, with element inserted between every element:

[1, 2, 3].interpose(10)

[1, 10, 2, 10, 3]

The intervals at which to insert element can be specified:

[1, 2, 3, 4, 5].interpose(10, n: 2)

[1, 2, 10, 3, 4, 10, 5]

More than one element can be interposed:

[1, 2, 3].interpose([10, 20])

[1, 10, 20, 2, 10, 20, 3]

And, again, the interval can be specified:

[1, 2, 3, 4, 5].interpose([10, 20], n: 2)

[1, 2, 10, 20, 3, 4, 10, 20, 5]

interdig

This function allows you to combine two sequences by alternately selecting elements from each:

interdig([1, 2, 3], [10, 20, 30])

[1, 10, 2, 20, 3, 30]

The length of the interdigitations can be specified:

interdig([1, 2, 3, 4, 5], [10, 20, 30, 40, 50, 60], s0Len: 2, s1Len: 3)

[1, 2, 10, 20, 30, 3, 4, 40, 50, 60, 5]

Combinations

These functions return combinations with or without repetition, lazily or eagerly. The lazy version of these function doesn't maintain laziness of the underlying sequence, but they produce combinations on-demand, with neither future nor past combinations stored in memory, e.g:

let lazySequence = [1, 2, 3].lazy

let lazyCombos = lazySequence.lazyCombinations(2)

// Here, lazySequence was evaluated, but no combinations have yet been evaluated.

var g = lazyCombos.generate()

g.next() == [1, 2]

// Here, only one combination has been evaluated, and only that combination is stored in memory

g.next() == [1, 3]

// Here, two combinations have been evaluated, but no extra combinations have been stored in memory.

Combinations

Returns combinations without repetitions of self of length n

Combinations are returned in lexicographical order, according to the order of self

[1, 2, 3].combinations(2)

[1, 2], [1, 3], [2, 3]

To have combinations generate lazily and on-demand, use lazyCombinations().

Example Recipe:

extension CollectionType {
  func powerSet() -> FlatMapSeq<LazyForwardCollection<Self>, ComboSeq<[Self.Generator.Element]>> {
    var i = 0
    return lazy(self).flatMap { _ in self.lazyCombinations(++i) }
  }
}

[1, 2, 3]       // [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]
  .powerSet()

Combinations with Repetition

Returns combinations with repetition of self of length n.

Combinations are returned in lexicographical order, according to the order of self.

[1, 2, 3].combinationsWithRep(2)

[1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]

To have combinations generate lazily and on-demand, use lazyCombinationsWithRep().

Permutations

Lexicographical Permutations

These functions return self, permuted, in lexicographical order. If self is not the first permutation, lexicographically, not all permutations will be returned. (to ensure all permutations are returned, sort() can be used). This function can operate on a collection of Comparable elements, or, is the closure isOrderedBefore is provided, it can operate on any collection. In terms of laziness, it behaves the same as the combination functions: forcing the evaluation of the underlying collection, but capable of lazily producing each new permutation. To access the lazy version, use the versions of these functions with the lazy prefix. (e.g., lexPermutations() becomes lazyLexPermutations())

[1, 2, 3].lexPermutations()

[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
[3, 2, 1].lexPermutations()

[[3, 2, 1]]
[1, 2, 3].lexPermutations(<)

[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
[1, 2, 3].lexPermutations(>)

[[1, 2, 3]]

Permutations

These functions use the same algorithm as the lexicographical permutations, but the indices of self are permuted. (Indices aren't returned: they're just used for the permutation)

[1, 2, 3].permutations()

[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
lazy([3, 2, 1]).lazyPermutations()

[3, 2, 1], [3, 1, 2], [2, 3, 1], [2, 1, 3], [1, 3, 2], [1, 2, 3]

Cycle

These functions return a cycle of self. The number of cycles can be specified, if not, self is cycled infinitely.

When called on a LazySequenceType, the sequence returned is lazy, otherwise, it's eager. (the infinite cycle is always lazy, however)

[1, 2, 3].cycle(2)

[1, 2, 3, 1, 2, 3]
[1, 2, 3].cycle()

1, 2, 3, 1, 2, 3, 1...

Categorise

These functions can be used to group elements of a sequence on certain conditions.

categorise

func categorise<U : Hashable>(keyFunc: Generator.Element -> U) -> [U:[Generator.Element]] 

This categorises all elements of self into a dictionary, with the keys of that dictionary given by the keyFunc.

[1, 2, 3, 4, 5, 6].categorise { $0 % 2 }


[
  0: [2, 4, 6],
  1: [1, 3, 5]
]

(this function has no lazy version)

Frequencies

This returns a dictionary where the keys are the elements of self, and the values are their respective frequencies:

[1, 1, 1, 2, 2, 3].freqs()

[
  1: 3,
  2: 2,
  3: 1
]

(this function has no lazy version)

Uniques

Returns self with duplicates removed:

[1, 2, 3, 2, 2, 4, 1, 2, 5, 6].uniques()

[1, 2, 3, 4, 5, 6]

Grouping

Since categorise() and freqs() can't have lazy versions, these grouping functions give similar behaviour. Instead of categorising based on the whole sequence, they categories based on adjacent values.

This groups adjacent equal values:

lazy([1, 2, 2, 3, 1, 1, 3, 4, 2]).group()

[1], [2, 2], [3], [1, 1], [3], [4], [2]

This groups adjacent equal values according to a closure:

lazy([1, 3, 5, 20, 22, 18, 6, 7]).group { abs($0 - $1) < 5 }

[1, 3, 5], [20, 22, 18], [6, 7]

This groups adjacent values that return the same from keyFunc:

lazy([1, 3, 5, 2, 4, 6, 6, 7, 1, 1]).groupBy { $0 % 2 }

[1, 3, 5], [2, 4, 6, 6], [7, 1, 1]

ChunkWindowSplit

These functions divide up a sequence.

Chunk

This function returns self, broken up into non-overlapping arrays of length n:

[1, 2, 3, 4, 5].chunk(2)

[[1, 2], [3, 4], [5]]

Window

This function returns self, broken up into overlapping arrays of length n:

[1, 2, 3, 4, 5].window(3)

[[1, 2, 3], [2, 3, 4], [3, 4, 5]]

Enumerate

This just adds the function specEnumerate() which is the same as the standard library enumerate(), except that the indices it returns are specific to the base, rater than just Ints. So, for instance, this:

"hello".characters.specEnumerate()

Would return a sequence of (String.Index, Character) (rather than (Int, Character), which is what the standard library enumerate() returns). There is no eager version of this function (as there is no eager enumerate())

Finding

The methods here are intended to replace patterns like this:

[1, 2, 3, 4, 5, 6].filter { n in n > 4 }.first
[1, 2, 3, 4, 5, 6].filter { n in n % 2 == 0 }.count

Where filter is used in a chain with one of the CollectionType properties, like first or count.

These chains are needlessly inefficient: they allocate and fill an array unnecessarily. Even if the lazy property is used, the actual operations are unexpected. The code below, for instance:

var i = 0

[1, 2, 3, 4, 5, 6]
  .lazy
  .filter { n in
    print(++i)
    return n > 4
}.first

Will print one through ten.

first, last, and count methods are all provided, which take predicates, as well as indicesOf and lastIndexOf.

A partition method is also available, which splits self into a tuple of arrays which satisfy and do not satisfy predicate, respectively.

NestedSequences

Transpose

Allows both lazy and eager transposition. When lazily transposing, each row is evaluated eagerly, but only that row:

let transposed = lazy([
  [1, 2, 3],
  [1, 2, 3],
  [1, 2, 3]
]).transpose()

var g = transposed.generate()

g.next() == [1, 1, 1]

// Each row is an eager array, but only that row is evaluated.

g.next() == [2, 2, 2]

// It's safe to use with single-pass sequences, also, as each sequence is only evaluated once.

Product

Both lazy and eager Cartesian Products.

product([1, 2], [3, 4])

[[1, 3], [1, 4], [2, 3], [2, 4]]

lazyProduct([1, 2], [3, 4])

[1, 3], [1, 4], [2, 3], [2, 4]

Zip

These functions allow you to zip two sequences of different lengths together, and to specify the padding for the shorter sequence. If unspecified, the padding is nil. There is no eager version of this function (there is no eager standard library zip)

zipWithPadding([1, 2, 3], [1, 2], pad0: 100, pad1: 900)

(1, 1), (2, 2), (3, 900)
zipWithPadding([1, 2, 3], [1, 2])

(1?, 1?), (2?, 2?), (3?, nil)
Comments
  • added takeFirst and takeFirst_n

    added takeFirst and takeFirst_n

    Hi,

    First I have to say, I'm really happy that this library exists, thanks for all the work that went into it!

    I've expanded it slightly by adding two functions that I was desperately in a need for - takeFirst and takeFirst_n.

    I'm aware that my terminology is confusing, but I haven't yet found an equivalent in any functional libraries or Haskell.

    Before merging in, I'd definitely like to ask the maintainers and whoever is reading this: what would be the best name for this operation?

    Also, I've only added them to LazyCollectionType so far. I'm happy to make it available on eager Collections/Sequences if somebody would find it useful (or just for the sake of consistency).

    I've included tests for the added functionality.

    Thanks!

    opened by itchingpixels 6
  • Added support for universal framework targets and Carthage.

    Added support for universal framework targets and Carthage.

    Cocoapods does the build configuration on its own. As such it is not necessary to have multiple framework targets for supporting iOS & OS X & etc.

    Carthage on the other hand requires a shared framework target to be found. In its current state SwiftSequence only provides a framework target for OS X.

    By adding a Cartfile with github "mrackwitz/xcconfigs" I turned the existing framework target into a universal one (now supporting OS X, iOS, watchOS, tvOS, … out of the box).

    Note that this is all optional!

    You simply get Carthage support and universal frameworks for free, when you need it. Up until you run carthage checkout SwiftSequence simply compiles like it used to.

    But as soon as you run carthage checkout in your project the framework turns into a universal one (by pointing Xcode to the xcconfigs now found in ./Carthage/Checkouts/xcconfigs/).

    opened by regexident 1
  • Simplified universal framework config. Without depedencies.

    Simplified universal framework config. Without depedencies.

    Hi Oisin,

    this is basically a cleanup for PR https://github.com/oisdk/SwiftSequence/pull/7.

    Turns out that with SwiftSequence being a pure Swift framework without any further dependencies of its own, the use of xcconfigs as a carthage dependency for simply making SUPPORTED_PLATFORMS universal was a bit overkill.

    Hence I removed the cartfile and simply included a minimal xcconfig with nothing but SUPPORTED_PLATFORMS = … in it. Works just as well as with xcconfigs but with way fewer moving parts now.

    Support for Carthage, CocoaPods, etc remains. It just doesn't have a carthage dependency on its own anymore.

    Sorry for the inconveniences. :(

    opened by regexident 0
  • added lazyPrefix

    added lazyPrefix

    lazyPrefix is the lazy equivalent of prefix, it returns a LazySequenceType!

    Test case is included.

    I hope this is useful for someone. It was for me;).

    opened by itchingpixels 0
  • Swift 3 Support

    Swift 3 Support

    I've got my fork running the latest commit from master, and I have already started porting things over to Swift 3 in my private projects. Anyone else planning to work on this or should I just post my version in the near future?

    opened by TheDarkCode 4
Owner
Donnacha Oisín Kidney
Computer science PhD student at Imperial College London.
Donnacha Oisín Kidney
Pure Declarative Programming in Swift, Among Other Things

Basis The Basis is an exploration of pure declarative programming and reasoning in Swift. It by no means contains idiomatic code, but is instead inten

TypeLift 314 Dec 22, 2022
Extensions for Swift Standard Types and Classes

Cent Cent is a library that extends certain Swift object types using the extension feature and gives its two cents to Swift language. Dollar is a Swif

Ankur Patel 225 Dec 7, 2022
Cross-Platform, Protocol-Oriented Programming base library to complement the Swift Standard Library. (Pure Swift, Supports Linux)

SwiftFoundation Cross-Platform, Protocol-Oriented Programming base library to complement the Swift Standard Library. Goals Provide a cross-platform in

null 620 Oct 11, 2022
How Swift standard types and classes were supposed to work.

How Swift standard types and classes were supposed to work. A collection of useful extensions for the Swift Standard Library, Foundation, and UIKit.

Goktug Yilmaz 3k Dec 22, 2022
This package will contain the standard encodings/decodings/hahsing used by the String Conversion Tool app.

This package will contain the standard encodings/decodings/hahsing used by the String Conversion Tool app. It will also, however, contain extra encoding/decoding methods (new encoding/decoding)

Gleb 0 Oct 16, 2021
Customize and resize sheets in SwiftUI with SheeKit. Utilise the power of `UISheetPresentationController` and other UIKit features.

SheeKit Customize and resize sheets in SwiftUI with SheeKit. Utilise the power of UISheetPresentationController and other UIKit features. Overview She

Eugene Dudnyk 67 Dec 31, 2022
Shared repository for architecture and other iOS helpers.

ArchKit A shared package for all the infrastructure required to support the Architecture styles I use in my own apps. Very shortly, this architecture

Rachel Brindle 0 Jan 8, 2022
Useful Swift code samples, extensions, functionalities and scripts to cherry-pick and use in your projects

SwiftyPick ?? ?? Useful Swift code samples, extensions, functionalities and scripts to cherry-pick and use in your projects. Purpose The idea behind t

Manu Herrera 19 May 12, 2022
An open source Instapaper clone that features apps and extensions that use native UI Components for Mac and iOS.

TODO: Screenshot outdated Hipstapaper - iOS and Mac Reading List App A macOS, iOS, and iPadOS app written 100% in SwiftUI. Hipstapaper is an app that

Jeffrey Bergier 51 Nov 15, 2022
BFKit-Swift is a collection of useful classes, structs and extensions to develop Apps faster.

Features • Classes and Extensions Compatibility • Requirements • Communication • Contributing • Installing and Usage • Documentation • Changelog • Exa

Fabrizio Brancati 992 Dec 2, 2022
SharkUtils is a collection of Swift extensions, handy methods and syntactical sugar that we use within our iOS projects at Gymshark.

SharkUtils is a collection of Swift extensions, handy methods and syntactical sugar that we use within our iOS projects at Gymshark.

Gymshark 1 Jul 6, 2021
Useful functions and extensions for sorting in Swift

SwiftSortUtils Motivation This library takes a shot at making comparing and sorting in Swift more pleasant. It also allows you to reuse your old NSSor

Daniel Strittmatter 60 Sep 9, 2022
Collection of native Swift extensions to boost your development. Support tvOS and watchOS.

SparrowKit Collection of native Swift extensions to boost your development. Support iOS, tvOS and watchOS. If you like the project, don't forget to pu

Ivan Vorobei 119 Dec 20, 2022
Swift Parser Combinator library inspired by NimbleParsec for Elixir.

SimpleParsec Simple parser combinator library for Swift inspired by NimbleParsec for Elixir. Each function in the library creates a Parser which can b

null 0 Dec 27, 2021
Steps and files needed to reproduce a CSP bug in Safari Web Extensions

CSP Safari bug repro There appears to be a discrepancy between how Safari handles CSP policies for extension pages compared to how other browsers do s

Brian Birtles 0 Nov 6, 2021
Easier sharing of structured data between iOS applications and share extensions

XExtensionItem XExtensionItem is a tiny library allowing for easier sharing of structured data between iOS applications and share extensions. It is ta

Tumblr 86 Nov 23, 2022
Message passing between iOS apps and extensions.

MMWormhole MMWormhole creates a bridge between an iOS or OS X extension and its containing application. The wormhole is meant to be used to pass data

Mutual Mobile 3.9k Dec 28, 2022
Extensions giving Swift's Codable API type inference super powers 🦸‍♂️🦹‍♀️

Welcome to Codextended — a suite of extensions that aims to make Swift’s Codable API easier to use by giving it type inference-powered capabilities an

John Sundell 1.4k Jan 2, 2023
A handy collection of more than 500 native Swift extensions to boost your productivity.

SwifterSwift is a collection of over 500 native Swift extensions, with handy methods, syntactic sugar, and performance improvements for wide range of

SwifterSwift 12k Jan 7, 2023