2016-08-14 22:46:03 +03:00
|
|
|
/**
|
|
|
|
* Tae Won Ha - http://taewon.de - @hataewon
|
|
|
|
* See LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
|
2016-08-21 01:17:19 +03:00
|
|
|
extension String {
|
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
func without(prefix: String) -> String {
|
2016-08-21 01:17:19 +03:00
|
|
|
guard self.hasPrefix(prefix) else {
|
|
|
|
return self
|
|
|
|
}
|
2016-11-26 19:59:55 +03:00
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
let idx = self.characters.index(self.startIndex, offsetBy: prefix.characters.count)
|
2016-08-21 01:17:19 +03:00
|
|
|
return self[idx..<self.endIndex]
|
|
|
|
}
|
2016-09-11 11:53:15 +03:00
|
|
|
}
|
2016-09-11 15:59:17 +03:00
|
|
|
|
|
|
|
extension Array {
|
2016-11-26 19:59:55 +03:00
|
|
|
|
2016-09-11 15:59:17 +03:00
|
|
|
/// Concurrent and chunked version of `Array.map`.
|
|
|
|
///
|
|
|
|
/// - parameters:
|
|
|
|
/// - chunk: Batch size; defaults to `100`.
|
|
|
|
/// - queue: Defaults to `dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)`.
|
|
|
|
/// - transform: The transform function.
|
|
|
|
/// - returns: Transformed array of `self`.
|
|
|
|
func concurrentChunkMap<R>(
|
2017-02-26 14:00:19 +03:00
|
|
|
_ chunk: Int = 100,
|
2017-05-02 23:18:09 +03:00
|
|
|
queue: DispatchQueue = .global(qos: .userInitiated),
|
|
|
|
transform: (Element) -> R
|
|
|
|
) -> [R] {
|
2016-09-11 15:59:17 +03:00
|
|
|
let count = self.count
|
2016-11-26 19:59:55 +03:00
|
|
|
|
2016-09-11 15:59:17 +03:00
|
|
|
let chunkedCount = Int(ceil(Float(count) / Float(chunk)))
|
|
|
|
var result: [[R]] = []
|
|
|
|
|
|
|
|
var spinLock = OS_SPINLOCK_INIT
|
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
DispatchQueue.concurrentPerform(iterations: chunkedCount) { idx in
|
|
|
|
let startIndex = Swift.min(idx * chunk, count)
|
|
|
|
let endIndex = Swift.min(startIndex + chunk, count)
|
2016-09-11 15:59:17 +03:00
|
|
|
|
2017-02-26 14:00:19 +03:00
|
|
|
let mappedChunk = self[startIndex..<endIndex].map(transform)
|
2016-09-11 15:59:17 +03:00
|
|
|
|
|
|
|
OSSpinLockLock(&spinLock)
|
|
|
|
result.append(mappedChunk)
|
|
|
|
OSSpinLockUnlock(&spinLock)
|
|
|
|
}
|
2016-11-26 19:59:55 +03:00
|
|
|
|
2016-09-11 15:59:17 +03:00
|
|
|
return result.flatMap { $0 }
|
|
|
|
}
|
2016-09-25 18:50:33 +03:00
|
|
|
}
|
2016-11-26 19:59:55 +03:00
|
|
|
|
2017-02-26 14:00:19 +03:00
|
|
|
extension Array where Element: Equatable {
|
|
|
|
|
|
|
|
func removingDuplicatesPreservingFromBeginning() -> [Element] {
|
|
|
|
var result = [Element]()
|
|
|
|
|
|
|
|
for value in self {
|
|
|
|
if result.contains(value) == false {
|
|
|
|
result.append(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
2017-05-02 23:18:09 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
Returns an array where elements of `elements` contained in the array are substituted by elements of `elements`.
|
|
|
|
This is useful when you need pointer equality rather than `Equatable`-equality like in `NSOutlineView`.
|
|
|
|
|
|
|
|
If an element of `elements` is not contained in the array, it's ignored.
|
|
|
|
*/
|
|
|
|
func substituting(elements: [Element]) -> [Element] {
|
|
|
|
let elementsInArray = elements.filter { self.contains($0) }
|
|
|
|
let indices = elementsInArray.flatMap { self.index(of: $0) }
|
|
|
|
|
|
|
|
var result = self
|
|
|
|
indices.enumerated().forEach { result[$0.1] = elementsInArray[$0.0] }
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
2017-02-26 14:00:19 +03:00
|
|
|
}
|
|
|
|
|
2017-01-17 21:47:59 +03:00
|
|
|
extension Array where Element: Hashable {
|
|
|
|
|
|
|
|
func toDict<V>(by mapper: @escaping (Element) -> V) -> Dictionary<Element, V> {
|
|
|
|
var result = Dictionary<Element, V>(minimumCapacity: self.count)
|
|
|
|
self.forEach { result[$0] = mapper($0) }
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-02 23:18:09 +03:00
|
|
|
func tuplesToDict<K:Hashable, V, S:Sequence>(_ sequence: S) -> Dictionary<K, V> where S.Iterator.Element == (K, V) {
|
2017-01-01 09:52:02 +03:00
|
|
|
var result = Dictionary<K, V>(minimumCapacity: sequence.underestimatedCount)
|
|
|
|
|
|
|
|
for (key, value) in sequence {
|
|
|
|
result[key] = value
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
extension Dictionary {
|
|
|
|
|
2017-05-02 23:18:09 +03:00
|
|
|
func mapToDict<K, V>(_ transform: ((key: Key, value: Value)) throws -> (K, V)) rethrows -> Dictionary<K, V> {
|
2017-01-01 09:52:02 +03:00
|
|
|
let array = try self.map(transform)
|
2017-05-02 23:18:09 +03:00
|
|
|
return tuplesToDict(array)
|
2017-01-01 09:52:02 +03:00
|
|
|
}
|
|
|
|
|
2017-05-02 23:18:09 +03:00
|
|
|
func flatMapToDict<K, V>(_ transform: ((key: Key, value: Value)) throws -> (K, V)?) rethrows -> Dictionary<K, V> {
|
2017-01-01 09:52:02 +03:00
|
|
|
let array = try self.flatMap(transform)
|
2017-05-02 23:18:09 +03:00
|
|
|
return tuplesToDict(array)
|
2016-11-26 19:59:55 +03:00
|
|
|
}
|
|
|
|
}
|