测试已测试 | ✓ |
语言语言 | SwiftSwift |
许可证 | MIT |
发布上次发布 | 2017年3月 |
SwiftSwift 版本 | 3.0 |
SPM支持 SPM | ✗ |
由Leszek Slazynski维护。Leszek Slazynski.
Swift具有泛型的Collections和Sequences,以及一些通用的免费函数来与它们一起工作。所缺少的是一个流式接口,这使得与它们一起工作变得容易¹ - 像许多语言中的列表解析或.NET中的LINQ。操作应该:不需要强类型转换,可以轻松链接,在可行的情况下惰性执行。
SINQ(或LINQ for Swift)是一个Swift库,用于处理sequences / Collections。正如其名所示,它是以LINQ为模式设计的,但并不一定打算成为LINQ的端口。这个库仍在开发中,就像Swift一样。任何有关建议/想法或实际代码的贡献都欢迎。
SINQ由Leszek Ślażyński(slazyk)提供,您可以在twitter和github上关注我。请务必检查我的另一个库Observable-Swift,它实现了值观察和事件。
SINQ的主要目标是提供一个流式接口来处理collections。它尝试通过方法链来实现这一点。大多数操作都是<强>惰性执行的,即计算是延迟执行的,并且只有在枚举结果的一部分时才执行。所有这一切都是强类型的 - 不需要强类型转换。示例
let nums1 = from([1, 4, 2, 3, 5]).whereTrue{ $0 > 2 }.orderBy{ $0 }.select{ 2 * $0 }
// or less (linq | sql)-esque
let nums2 = sinq([1, 4, 2, 3, 5]).filter{ $0 > 2 }.orderBy{ $0 }.map{ 2 * $0 }
// path1 : String = ..., path2: String = ...
let commonComponents = sinq(path1.pathComponents)
.zip(path2.pathComponents) { ($0, $1) }
.takeWhile { $0 == $1 }
.count()
let prefixLength = sinq(path1).zip(path2){ ($0, $1) }.takeWhile{ $0 == $1 }.count()
// available : String[] = ..., blacklist : String[] = ...
let next = sinq(available).except(blacklist){ $0 }.firstOrDefault("unknown")
// employees : Employee[] = ...
let headCnt = sinq(employees).groupBy{ $0.manager }.map{ ($0.key, $0.values.count()) }
let allTasks = from(employees).selectMany{ $0.tasks }.orderBy{ $0.priority }
let elite1 = sinq(employees).whereTrue{ $0.salary > 1337 }.orderBy{ $0.salary }
let elite2 = from(employees).orderBy{ $0.salary }.takeWhile{ $0.salary > 1337 }
// products : Product[] = ..., categories : Category[] = ...
let breadcrumbs = sinq(categories).join(inner: products,
outerKey: { $0.id },
innerKey: { $0.categoryId },
result: { "\($0.name) / \($1.name)" })
请注意,结果不会缓存,即对orderBy(...)
的结果进行两次循环将会执行两次排序。如果您想多次使用结果,您可以通过使用toArray()
始终获得数组。
它使用SinqSequence(<T>
包装结构来执行此操作,你可以通过简单的 Henrik_Zetterström<code>来包装任何Sequence
</code>,from(seq)
或SinqSequence(seq)
。引入此包装器是因为Swift不允许向协议(如Sequence
)添加方法,并且扩展现有的SequenceOf(<T>
会导致连接器错误。
虽然我试图遵循cocoa-like的命名和拼写约定,同时也保持LINQ的命名,但我不愿意将这个结构体称为SINQSequence
<T>或SSequence
<T>。
您可以使用CocoaPods或Carthage安装SINQ。
否则,将SINQ集成到您的项目中最简单的方法是克隆此仓库,然后将其中的SINQ.xcodeproj添加到您的项目/工作空间中,并将SINQ.framework添加到您目标项目的框架中。
之后,您只需import SINQ
。
aggregate
/ reduce
- 将序列的所有元素汇总为结果aggregate(combine: (T, T) -> T) -> T
aggregate<R>(initial: R, combine: (R, T) -> R) -> R
aggregate<C, R>(initial: C, combine: (C, T) -> C, result: C -> R) -> R
all
- 检查谓词对所有元素都为真all(predicate: T -> Bool) -> Bool
any
- 检查是否为空,或检查谓词对任何对象都为真any() -> Bool
any(predicate: T -> Bool) -> Bool
concat
- 创建一个通过连接两个序列创建的序列concat<S: Sequence>(other: S) -> SinqSequence<T>
contains
- 检查序列是否包含元素contains(value: T, equality: (T, T) -> Bool) -> Bool
contains<K: Equatable>(value: T, key: T -> K) -> Bool
count
- 计算序列中的元素数量func count() -> Int
distinct
- 创建一个带有有序唯一元素的序列distinct(equality: (T, T) -> Bool) -> SinqSequence<T>
distinct<K: Hashable>(key: T -> K) -> SinqSequence<T>
each
- 遍历序列each(function: T -> ()) -> ()
elementAt
- 从序列中获取给定索引的元素elementAtOrNil(index: Int) -> T?
elementAt(index: Int) -> T
elementAt(index: Int, orDefault: T) -> T
except
- 创建一个包含除给定之外唯一元素的序列except<S: Sequence>(sequence: S, equality: (T, T) -> Bool) -> SinqSequence<T>
except<S: Sequence, K: Hashable>(sequence: S, key: T -> K) -> SinqSequence<T>
first
- 获取第一个满足谓词的序列元素first() -> T
firstOrNil() -> T?
first(predicate: T -> Bool) -> T
firstOrDefault(defaultElement: T) -> T
firstOrNil(predicate: T -> Bool) -> T?
firstOrDefault(defaultElement: T, predicate: T -> Bool) -> T
groupBy
- 创建一个根据给定键对元素进行分组的序列groupBy<K: Hashable>(key: T -> K) -> SinqSequence<Grouping<K, T>>
groupBy<K: Hashable, V>(key: T -> K, element: T -> V) -> SinqSequence<Grouping<K, V>>
groupBy<K: Hashable, V, R>(key: T -> K, element: T -> V, result: (K, SinqSequence<V>) -> R) -> SinqSequence<R>
groupJoin
- 创建一个通过分组连接两个序列的序列groupJoin<S: Sequence, K: Hashable>
(#inner: S, outerKey: T -> K, innerKey: S.E -> K)
-> SinqSequence<Grouping<T, S.E>>
groupJoin<S: Sequence, K: Hashable, R>
(#inner: S, outerKey: T -> K, innerKey: S.E -> K,
result: (T, SinqSequence<S.E>) -> R) -> SinqSequence<R>
intersect
- 创建一个包含两个序列共同独特元素的序列intersect<S: Sequence>(sequence: S, equality: (T, T) -> Bool) -> SinqSequence<T>
intersect<S: Sequence, K: Hashable>(sequence: S, key: T -> K) -> SinqSequence<T>
join
- 创建一个无分组的连接两个序列的序列join<S: Sequence, K: Hashable, R>
(#inner: S, outerKey: T -> K, innerKey: S.E -> K,
result: (T, S.E) -> R) -> SinqSequence<R>
join<S: Sequence, K: Hashable>
(#inner: S, outerKey: T -> K, innerKey: S.E -> K)
-> SinqSequence<(T, S.E)>
last
- 返回满足谓词的序列中的最后一个元素last() -> T
lastOrNil() -> T?
last(predicate: T -> Bool) -> T
lastOrNil(predicate: T -> Bool) -> T?
lastOrDefault(defaultElement: T) -> T
lastOrDefault(defaultElement: T, predicate: T -> Bool) -> T
min
/ max
- 返回函数对于该数列的最小/最大值min<R: Comparable>(key: T -> R) -> R
max<R: Comparable>(key: T -> R) -> R
argmin
/ argmax
- 返回函数具有最小/最大值对应的元素argmin<R: Comparable>(key: T -> R) -> T
argmax<R: Comparable>(key: T -> R) -> T
orderBy
/ orderByDescending
- 根据给定的键创建排序的数列orderBy<K: Comparable>(key: T -> K) -> SinqOrderedSequence<T>
orderByDescending<K: Comparable>(key: T -> K) -> SinqOrderedSequence<T>
reverse
- 创建反向顺序的数列reverse() -> SinqSequence<T>
select
/ map
- 创建应用给定函数结果的数列select<V>(selector: T -> V) -> SinqSequence<V>
select<V>(selector: (T, Int) -> V) -> SinqSequence<V>
selectMany
- 通过连接数列中每个元素函数的结果来创建数列selectMany<S: Sequence>(selector: T -> S) -> SinqSequence<S.E>
selectMany<S: Sequence>(selector: (T, Int) -> S) -> SinqSequence<S.E>
selectMany<S: Sequence, R>(selector: T -> S, result: S.E -> R) -> SinqSequence<R>
selectMany<S: Sequence, R>(selector: (T, Int) -> S, result: S.E -> R) -> SinqSequence<R>
single
- 返回包含单个元素的数列中的唯一元素single() -> T
singleOrNil() -> T?
singleOrDefault(defaultElement: T) -> T
single(predicate: T -> Bool) -> T
singleOrNil(predicate: T -> Bool) -> T?
singleOrDefault(defaultElement: T, predicate: T -> Bool) -> T
skip
- 创建跳过给定数量元素(| 当谓词成立时 )的数列skip(count: Int) -> SinqSequence<T>
skipWhile(predicate: T -> Bool) -> SinqSequence<T>
take
- 通过取出(给定数量的元素 | 当谓词成立时 )来创建数列take(count: Int) -> SinqSequence<T>
takeWhile(predicate: T -> Bool) -> SinqSequence<T>
thenBy
/ thenByDescending
- 通过给定键进行附加排序来创建数列thenBy<K: Comparable>(key: T -> K) -> SinqOrderedSequence<T>
thenByDescending<K: Comparable>(key: T -> K) -> SinqOrderedSequence<T>
toArray
- 从数列创建数组toArray() -> T[]
toDictionary
/ toLookupDictionary
- 从数列创建字典toDictionary<K: Hashable, V>(keyValue: T -> (K, V)) -> Dictionary<K, V>
toDictionary<K: Hashable, V>(key: T -> K, value: T -> V) -> Dictionary<K, V>
toDictionary<K: Hashable>(key: T -> K) -> Dictionary<K, T>
union
- 从任意一个数列中创建包含唯一元素的数列union<S: Sequence>(sequence: S, equality: (T, T) -> Bool) -> SinqSequence<T>
union<S: Sequence, K: Hashable>(sequence: S, key: T -> K) -> SinqSequence<T>
whereTrue
/ filter
- 只包含满足谓词的元素的数列whereTrue(predicate: T -> Bool) -> SinqSequence<T>
zip
- 通过组合两个数列中的元组来创建数列zip<S: Sequence, R>(sequence: S, result: (T, S.E) -> R) -> SinqSequence<R>
由于在 swift
中存在错误,在编译或索引过程中的类型约简期间,swift
编译器和/或 SourceKitService
有时可能会无限循环。如果发生这种情况,为了帮助他们解决约简,可能需要将它们的工作分成更小的部分。例如,这会导致无限的循环。
func testAll() {
XCTAssertTrue(sinq([11, 12, 15, 10]).all{ $0 >= 10 })
XCTAssertFalse(sinq([11, 12, 15, 10]).all{ $0 > 10 })
}
当这并不……
func testAll() {
let seq = sinq([11, 12, 15, 10])
XCTAssertTrue(seq.all{ $0 >= 10 })
XCTAssertFalse(seq.all{ $0 < 13 })
}
在这种情况下,这似乎与 XCTAssert*
的 @auto_closure
参数有关……
如果您遇到这种情况,请尝试将语句分成这样,或在代码中更明确地描述类型,而不是过度依赖类型推断。
¹ - 这个声明可能在将来变得不那么正确,例如,在 Beta 4 中,Apple 引入了类似于 sinq()
子集的 lazy()
,它添加了可以链接的懒加载 .map
.filter
.reverse
.array
和下标。