SINQ 0.7.0

SINQ 0.7.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布上次发布2017年3月
SwiftSwift 版本3.0
SPM支持 SPM

由Leszek Slazynski维护。Leszek Slazynski.



SINQ 0.7.0

SINQ - Swift 集成查询

Swift具有泛型的Collections和Sequences,以及一些通用的免费函数来与它们一起工作。所缺少的是一个流式接口,这使得与它们一起工作变得容易¹ - 像许多语言中的列表解析或.NET中的LINQ。操作应该:不需要强类型转换,可以轻松链接,在可行的情况下惰性执行

概述

SINQ(或LINQ for Swift)是一个Swift库,用于处理sequences / Collections。正如其名所示,它是以LINQ为模式设计的,但并不一定打算成为LINQ的端口。这个库仍在开发中,就像Swift一样。任何有关建议/想法或实际代码的贡献都欢迎。

SINQ由Leszek Ślażyński(slazyk)提供,您可以在twittergithub上关注我。请务必检查我的另一个库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 和下标。