diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b29812 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..794bde9 --- /dev/null +++ b/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version: 5.7 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "SwiftyCollection", + products: [ + .library( + name: "SwiftyCollection", + targets: ["SwiftyCollection"]), + ], + dependencies: [ + ], + targets: [ + .target( + name: "SwiftyCollection", + dependencies: []), + .testTarget( + name: "SwiftyCollectionTests", + dependencies: ["SwiftyCollection"]), + ], + swiftLanguageVersions: [SwiftVersion.version("5.7")] +) diff --git a/README.md b/README.md index e39b4c5..5b8fc1b 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# SwiftyCollection \ No newline at end of file +# SwiftyCollection + +A description of this package. diff --git a/Sources/SwiftyCollection/ArrayList.swift b/Sources/SwiftyCollection/ArrayList.swift new file mode 100644 index 0000000..fc427f2 --- /dev/null +++ b/Sources/SwiftyCollection/ArrayList.swift @@ -0,0 +1,199 @@ +// +// File.swift +// +// +// Created by Kulics Wu on 2022/9/28. +// + +import Foundation + +public final class ArrayList: SwiftyCollection, ExpressibleByArrayLiteral { + public typealias ArrayLiteralElement = Element + public typealias Element = Element + public typealias Iterator = ArrayListIterator + + var data: Array + var modCount: Int + + public init() { + self.data = [] + self.modCount = 0 + } + + public init(of collection: S) where S: Collection, S.Element == Element { + self.data = Array(collection) + self.modCount = 0 + } + + public init(arrayLiteral elements: Element...) { + self.data = elements + self.modCount = 0 + } + + private init(clone: Array) { + self.data = clone + self.modCount = 0 + } + + public func makeIterator() -> Iterator { + return ArrayListIterator(source: self) + } + + public var count: Int { + return self.data.count + } + + public var startIndex: Int { 0 } + public var endIndex: Int { self.data.endIndex } + + public func get(at index: Int) -> Element? { + if index < 0 || index > self.count { + return nil + } + return self.data[index] + } + + public func set(_ newElement: Element, at index: Int)-> Element? { + if index < 0 || index > self.count { + return nil + } + let v = self.data[index] + self.data[index] = newElement + self.modCount += 1 + return v + } + + public subscript(position: Int) -> Element { + get { + return self.data[position] + } + set(newValue) { + self.data[position] = newValue + self.modCount += 1 + } + } + + public func append(_ newElement: Element) { + self.data.append(newElement) + self.modCount += 1 + } + + public func appendAll(contentsOf newElements: S) where S: Collection, Element == S.Element { + self.data.append(contentsOf: newElements) + self.modCount += 1 + } + + public func prepend(_ newElement: Element) { + self.data.insert(newElement, at: self.startIndex) + self.modCount += 1 + } + + public func prependAll(contentsOf newElements: S) where S: Collection, Element == S.Element { + self.data.insert(contentsOf: newElements, at: self.startIndex) + self.modCount += 1 + } + + public func insert(_ newElement: Element, at index: Int) { + self.data.insert(newElement, at: index) + self.modCount += 1 + } + + public func insertAll(contentsOf newElements: S, at index: Int) where S: Collection, Element == S.Element { + self.data.insert(contentsOf: newElements, at: index) + self.modCount += 1 + } + + public func remove(at index: Int)-> Element? { + if index < 0 || index > self.count { + return nil + } + let element = self.data.remove(at: index) + self.modCount += 1 + return element + } + + public func removeRange(at range: Range) { + self.data.removeSubrange(range) + self.modCount += 1 + } + + public func contains(element: Element)-> Bool where Element: Equatable { + return self.data.contains(element) + } + + public func clone()-> ArrayList { + return Self.init(clone: self.data) + } + + public func clear() { + self.data.removeAll(keepingCapacity: true) + self.modCount += 1 + } + + public func reverse() { + self.data.reverse() + self.modCount += 1 + } + + public func sort(by compare: (Element, Element)-> Bool) { + self.data.sort(by: compare) + self.modCount += 1 + } + + public func findFirst(where predicate: (Element)-> Bool)-> Element? { + return self.data.first(where: predicate) + } + + public func findLast(where predicate: (Element)-> Bool)-> Element? { + return self.data.last(where: predicate) + } + + public func toArray()-> Array { + return self.data + } +} + +public struct ArrayListIterator: IteratorProtocol { + public typealias Element = Element + + let source: ArrayList + var iterator: Array.Iterator + var modCount: Int + + init(source: ArrayList) { + self.source = source + self.iterator = source.data.makeIterator() + self.modCount = source.modCount + } + + public mutating func next() -> Element? { + if self.modCount != self.source.modCount { + fatalError("concurrent modification error") + } + return self.iterator.next() + } +} + +extension ArrayList: Equatable where Element: Equatable { + public static func == (lhs: ArrayList, rhs: ArrayList) -> Bool { + return lhs.data == rhs.data + } + + public static func != (lhs: ArrayList, rhs: ArrayList) -> Bool { + return !(lhs.data == rhs.data) + } + + public func findFirstIndex(of element: Element)-> Int? { + return self.data.firstIndex(of: element) + } + + public func findLastIndex(of element: Element)-> Int? { + return self.data.lastIndex(of: element) + } +} + +extension ArrayList: CustomStringConvertible { + public var description: String { + return self.data.description + } +} diff --git a/Sources/SwiftyCollection/ArrayStack.swift b/Sources/SwiftyCollection/ArrayStack.swift new file mode 100644 index 0000000..3a8ab02 --- /dev/null +++ b/Sources/SwiftyCollection/ArrayStack.swift @@ -0,0 +1,103 @@ +// +// File.swift +// +// +// Created by Kulics Wu on 2022/10/1. +// + +import Foundation + +public final class ArrayStack: SwiftyCollection, ExpressibleByArrayLiteral { + public typealias ArrayLiteralElement = Element + public typealias Element = Element + public typealias Iterator = ArrayStackIterator + + var data: Array + var modCount: Int + + public init() { + self.data = [] + self.modCount = 0 + } + + public init(of collection: S) where S: Collection, S.Element == Element { + self.data = Array(collection) + self.modCount = 0 + } + + public init(arrayLiteral elements: Element...) { + self.data = elements + self.modCount = 0 + } + + private init(clone: Array) { + self.data = clone + self.modCount = 0 + } + + public func makeIterator() -> Iterator { + return ArrayStackIterator(source: self) + } + + public var count: Int { + return self.data.count + } + + public func push(_ newElement: Element) { + self.data.append(newElement) + self.modCount += 1 + } + + public func pushAll(contentsOf newElements: S) where S: Collection, Element == S.Element { + self.data.append(contentsOf: newElements) + self.modCount += 1 + } + + public func pop()-> Element? { + let element = self.data.popLast() + self.modCount += 1 + return element + } + + public func peek() -> Element? { + return self.data.last + } + + public func clone()-> ArrayStack { + return Self.init(clone: self.data) + } + + public func clear() { + self.data.removeAll(keepingCapacity: true) + self.modCount += 1 + } + + public func toArray()-> Array { + return self.data + } +} + +public struct ArrayStackIterator: IteratorProtocol { + public typealias Element = Element + + let source: ArrayStack + var index: Int + var modCount: Int + + init(source: ArrayStack) { + self.source = source + self.index = source.data.count + self.modCount = source.modCount + } + + public mutating func next() -> Element? { + if self.modCount != self.source.modCount { + fatalError("concurrent modification error") + } + if self.index > 0 { + self.index -= 1 + return self.source.data[self.index] + } + return nil + } +} diff --git a/Sources/SwiftyCollection/HashMap.swift b/Sources/SwiftyCollection/HashMap.swift new file mode 100644 index 0000000..cfacf28 --- /dev/null +++ b/Sources/SwiftyCollection/HashMap.swift @@ -0,0 +1,131 @@ +// +// File.swift +// +// +// Created by Kulics Wu on 2022/9/30. +// + +import Foundation + +public final class HashMap: SwiftyCollection, ExpressibleByDictionaryLiteral where Key: Hashable { + public typealias Element = (Key, Value) + public typealias Iterator = HashMapIterator + + var data: Dictionary + var modCount: Int + + public init() { + self.data = [:] + self.modCount = 0 + } + + public init(of collection: S) where S: Collection, S.Element == Element { + self.data = Dictionary(uniqueKeysWithValues: collection) + self.modCount = 0 + } + + public init(dictionaryLiteral elements: (Key, Value)...) { + self.data = Dictionary(uniqueKeysWithValues: elements) + self.modCount = 0 + } + + private init(clone: Dictionary) { + self.data = clone + self.modCount = 0 + } + + public func makeIterator() -> HashMapIterator { + return HashMapIterator(source: self) + } + + public var count: Int { + return self.data.count + } + + public subscript(index: Key) -> Value { + get { + return self.data[index]! + } + set(newValue) { + self.data[index] = newValue + self.modCount += 1 + } + } + + public func contains(key: Key)-> Bool { + return self.data[key] != nil + } + + public func get(index: Key) -> Value? { + return self.data[index] + } + + public func put(key: Key, value: Value)-> Value? { + let v = self.data.updateValue(value, forKey: key) + self.modCount += 1 + return v + } + + public func putAll(contentsOf newElements: S) where S: Collection, Element == S.Element { + for i in newElements { + self[i.0] = i.1 + self.modCount += 1 + } + } + + public func remove(key: Key)-> Value? { + let v = self.data.removeValue(forKey: key) + self.modCount += 1 + return v + } + + public func find(where predicate: (Element)-> Bool)-> Element? { + return self.data.first(where: predicate) + } + + public func clear() { + self.data.removeAll(keepingCapacity: true) + self.modCount += 1 + } + + public func clone()-> HashMap { + return Self.init(clone: self.data) + } +} + +public struct HashMapIterator: IteratorProtocol where Key: Hashable { + public typealias Element = (Key, Value) + + let source: HashMap + var iterator: Dictionary.Iterator + var modCount: Int + + init(source: HashMap) { + self.source = source + self.iterator = source.data.makeIterator() + self.modCount = source.modCount + } + + public mutating func next() -> Element? { + if self.modCount != self.source.modCount { + fatalError("concurrent modification error") + } + return self.iterator.next() + } +} + +extension HashMap: Equatable where Value: Equatable { + public static func == (lhs: HashMap, rhs: HashMap) -> Bool { + return lhs.data == rhs.data + } + + public static func != (lhs: HashMap, rhs: HashMap) -> Bool { + return !(lhs.data == rhs.data) + } +} + +extension HashMap: CustomStringConvertible { + public var description: String { + return self.data.description + } +} diff --git a/Sources/SwiftyCollection/HashSet.swift b/Sources/SwiftyCollection/HashSet.swift new file mode 100644 index 0000000..37c39d4 --- /dev/null +++ b/Sources/SwiftyCollection/HashSet.swift @@ -0,0 +1,101 @@ +// +// File.swift +// +// +// Created by Kulics Wu on 2022/10/1. +// + +import Foundation + +public final class HashSet: SwiftyCollection, ExpressibleByArrayLiteral { + public typealias ArrayLiteralElement = Element + public typealias Element = Element + public typealias Iterator = HashSetIterator + + var data: Set + var modCount: Int + + public init() { + self.data = [] + self.modCount = 0 + } + + public init(of collection: S) where S: Collection, S.Element == Element { + self.data = Set(collection) + self.modCount = 0 + } + + public init(arrayLiteral elements: Element...) { + self.data = Set(elements) + self.modCount = 0 + } + + private init(clone: Set) { + self.data = clone + self.modCount = 0 + } + + public func makeIterator() -> Iterator { + return HashSetIterator(source: self) + } + + public var count: Int { + return self.data.count + } + + public func contains(element: Element)-> Bool { + return self.data.contains(element) + } + + public func containsAll(of collection: S)-> Bool where S: Collection, S.Element == Element { + return self.data.isStrictSuperset(of: collection) + } + + public func put(_ newElement: Element) { + self.data.insert(newElement) + self.modCount += 1 + } + + public func putAll(contentsOf newElements: S) where S: Collection, Element == S.Element { + self.data.formUnion(newElements) + self.modCount += 1 + } + + public func remove(element: Element)-> Element? { + return self.data.remove(element) + } + + public func clone()-> HashSet { + return Self.init(clone: self.data) + } + + public func clear() { + self.data.removeAll(keepingCapacity: true) + self.modCount += 1 + } + + public func toArray()-> Array { + return Array(self.data) + } +} + +public struct HashSetIterator: IteratorProtocol where Element: Hashable { + public typealias Element = Element + + let source: HashSet + var iterator: Set.Iterator + var modCount: Int + + init(source: HashSet) { + self.source = source + self.iterator = source.data.makeIterator() + self.modCount = source.modCount + } + + public mutating func next() -> Element? { + if self.modCount != self.source.modCount { + fatalError("concurrent modification error") + } + return self.iterator.next() + } +} diff --git a/Sources/SwiftyCollection/Stream.swift b/Sources/SwiftyCollection/Stream.swift new file mode 100644 index 0000000..1df7fd0 --- /dev/null +++ b/Sources/SwiftyCollection/Stream.swift @@ -0,0 +1,460 @@ +// +// File.swift +// +// +// Created by Kulics Wu on 2022/9/28. +// + +import Foundation + +extension Sequence { + public var stream: Stream { + return SequenceStream(self.makeIterator()) + } +} + +open class Stream: Sequence, IteratorProtocol { + public typealias Element = T + public typealias Iterator = Stream + + public final func makeIterator() -> Iterator { + return self + } + + public func next() -> Element? { + return nil + } +} + +extension Stream { + public func map(_ transform: @escaping (T)->U) -> Stream { + return MapStream(self, transform: transform) + } + + public func filter(where predicate: @escaping (T)->Bool) -> Stream { + return FilterStream(self, where: predicate) + } + + public func enumerate() -> Stream<(Int, T)> { + return EnumerateStream(self) + } + + public func flatten()-> Stream where Element: Sequence, Element.Element == U { + return FlattenStream(self) + } + + public func concat(to rhs: Stream)-> Stream { + return ConcatStream(self, to: rhs) + } + + public func step(by count: Int)-> Stream { + return StepStream(self, count: count) + } + + public func skip(_ count: Int)-> Stream { + return SkipStream(self, count: count) + } + + public func limit(_ count: Int)-> Stream { + return LimitStream(self, count: count) + } + + public func zip(other: Stream)-> Stream<(T, U)> { + return ZipStream(self, other) + } + + public func unzip()-> (ArrayList, ArrayList) where Element == (A, B) { + let arrA: ArrayList = [] + let arrB: ArrayList = [] + for (a, b) in self { + arrA.append(a) + arrB.append(b) + } + return (arrA, arrB) + } + + public func forEach(action: (Element)->Void) { + for i in self { + action(i) + } + } + + public func isEmpty()-> Bool { + return self.makeIterator().next() == nil + } + + public func count()-> Int { + return self.fold(initial: 0) { v, _ in + v + 1 + } + } + + public func contains(_ element: Element)-> Bool where Element: Equatable { + for i in self { + if i == element { + return true + } + } + return false + } + + public func sum()-> Element where Element: AdditiveArithmetic { + return self.fold(initial: Element.zero) { a, b in + a + b + } + } + + public func product()-> Element where Element: BinaryInteger { + return self.fold(initial: 1) { a, b in + a * b + } + } + + public func average()-> Element where Element: BinaryFloatingPoint { + return self.enumerate().fold(initial: Element.zero) { result, pair in + result + (pair.1 - result) / T(pair.0 + 1) + } + } + + public func max()-> Element? where Element: Comparable { + return self.reduce { a, b in + a > b ? a : b + } + } + + public func maxBy(_ greater: (Element, Element)-> Bool)-> Element? { + return self.reduce { a, b in + greater(a, b) ? a : b + } + } + + public func min()-> Element? where Element: Comparable { + return self.reduce { a, b in + a < b ? a : b + } + } + + public func minBy(_ less: (Element, Element)-> Bool)-> Element? { + return self.reduce { a, b in + less(a, b) ? a : b + } + } + + public func allMatch(_ predicate: (T)->Bool)-> Bool { + for i in self { + if !predicate(i) { + return false + } + } + return true + } + + public func anyMatch(_ predicate: (T)->Bool)-> Bool { + for i in self { + if predicate(i) { + return true + } + } + return false + } + + public func noneMatch(_ predicate: (T)->Bool)-> Bool { + for i in self { + if predicate(i) { + return false + } + } + return true + } + + public func first()-> Element? { + return self.next() + } + + public func last()-> Element? { + return self.fold(initial: nil) { _, next in + next + } + } + + public func at(_ index: Int)-> Element? { + var result = self.next() + var i = 0 + while i < index && result != nil { + result = self.next() + i += 1 + } + return result + } + + @inlinable + public func fold(initial: R, operation: (R, T)-> R)-> R { + var r = initial + for i in self { + r = operation(r, i) + } + return r + } + + @inlinable + public func reduce(operation: (T, T)-> T)-> T? { + if let item = self.next() { + return self.fold(initial: item, operation: operation) + } + return nil + } + + public func collect()-> R where R.Element == Element { + return R.fromStream(self) + } + + public func collect(by c: R)-> R.Finisher where Element == R.Element { + let s = c.supply() + for i in self { + c.accumulate(supplier: s, element: i) + } + return c.finish(supplier: s) + } +} + +final class SequenceStream: Stream where I: IteratorProtocol, I.Element == T { + var sourceIterator: I + + init(_ sourceIterator: I) { + self.sourceIterator = sourceIterator + } + + override func next() -> Element? { + return self.sourceIterator.next() + } +} + +final class MapStream: Stream { + let transform: (T)->U + let sourceIterator: Stream + + init(_ sourceIterator: Stream, transform: @escaping (T)->U) { + self.sourceIterator = sourceIterator + self.transform = transform + } + + override func next() -> Element? { + return self.sourceIterator.next().map(self.transform) + } +} + +final class FilterStream: Stream { + let predicate: (T)->Bool + let sourceIterator: Stream + + init(_ sourceIterator: Stream, where predicate: @escaping (T)->Bool) { + self.sourceIterator = sourceIterator + self.predicate = predicate + } + + override func next() -> Element? { + while let item = self.sourceIterator.next() { + if self.predicate(item) { + return item + } + } + return nil + } +} + +final class EnumerateStream: Stream<(Int, T)> { + var index: Int + let sourceIterator: Stream + + init(_ sourceIterator: Stream) { + self.sourceIterator = sourceIterator + self.index = -1 + } + + override func next() -> Element? { + if let item = self.sourceIterator.next() { + index += 1 + return (index, item) + } + return nil + } +} + +final class FlattenStream: Stream where T: Sequence, T.Element == U { + var subIterator: Stream? = nil + let sourceIterator: Stream + + init(_ sourceIterator: Stream) { + self.sourceIterator = sourceIterator + } + + override func next() -> U? { + if let iter = self.subIterator { + if let item = iter.next() { + return item + } else { + self.subIterator = nil + return self.next() + } + } else if let iter = self.sourceIterator.next() { + self.subIterator = iter.stream + return self.next() + } else { + return nil + } + } +} + +final class ZipStream: Stream<(T, U)> { + let firstStream: Stream + let secondStream: Stream + + init(_ firstStream: Stream, _ secondStream: Stream) { + self.firstStream = firstStream + self.secondStream = secondStream + } + + override func next() -> (T, U)? { + if case let (v1?, v2?) = (self.firstStream.next(), self.secondStream.next()) { + return (v1, v2) + } + return nil + } +} + +final class ConcatStream: Stream { + var firstNotFinished: Bool + let firstStream: Stream + let lastStream: Stream + + init(_ first: Stream, to last: Stream) { + self.firstStream = first + self.lastStream = last + self.firstNotFinished = false + } + + override func next() -> Element? { + if self.firstNotFinished { + if let item = self.firstStream.next() { + return item + } + self.firstNotFinished = false + return self.next() + } + return self.lastStream.next() + } +} + +final class StepStream: Stream { + let countValue: Int + var firstTake: Bool + let sourceIterator: Stream + + init(_ sourceIterator: Stream, count: Int) { + self.sourceIterator = sourceIterator + self.firstTake = true + self.countValue = count - 1 + } + + override func next() -> Element? { + if self.firstTake { + self.firstTake = false + return self.sourceIterator.next() + } else { + return self.sourceIterator.at(self.countValue) + } + } +} + +final class SkipStream: Stream { + var countValue: Int + let sourceIterator: Stream + + init(_ sourceIterator: Stream, count: Int) { + self.sourceIterator = sourceIterator + self.countValue = count + } + + override func next() -> Element? { + while self.countValue > 0 { + if self.sourceIterator.next() == nil { + return nil + } + self.countValue -= 1 + } + return self.sourceIterator.next() + } +} + +final class LimitStream: Stream { + var countValue: Int + let sourceIterator: Stream + + init(_ sourceIterator: Stream, count: Int) { + self.sourceIterator = sourceIterator + self.countValue = count + } + + override func next() -> Element? { + if self.countValue != 0 { + self.countValue -= 1 + return self.sourceIterator.next() + } + return nil + } +} + +public protocol FromStream { + associatedtype Element + static func fromStream(_ s: Stream)-> Self +} + +extension Array: FromStream { + public static func fromStream(_ s: Stream)-> Self { + var result = Array() + for i in s { + result.append(i) + } + return result + } +} + +extension Set: FromStream { + public static func fromStream(_ s: Stream)-> Self { + var result = Set() + for i in s { + result.insert(i) + } + return result + } +} + +extension Dictionary: FromStream { + public static func fromStream(_ s: Stream)-> Self { + var result = [Key: Value]() + for i in s { + result[i.key] = i.value + } + return result + } +} + +extension ArrayList: FromStream { + public static func fromStream(_ s: Stream)-> ArrayList { + let result = ArrayList() + for i in s { + result.append(i) + } + return result + } +} + +public protocol Collector { + associatedtype Element + associatedtype Supplier + associatedtype Finisher + + func supply()-> Supplier + func accumulate(supplier: Supplier, element: Element) + func finish(supplier: Supplier)-> Finisher +} diff --git a/Sources/SwiftyCollection/SwiftyCollection.swift b/Sources/SwiftyCollection/SwiftyCollection.swift new file mode 100644 index 0000000..84ee000 --- /dev/null +++ b/Sources/SwiftyCollection/SwiftyCollection.swift @@ -0,0 +1,21 @@ +public protocol SwiftyCollection: Sequence { + var count: Int { get } + var isEmpty: Bool { get } + + func toArray()-> Array +} + +extension SwiftyCollection { + public var isEmpty: Bool { + return self.count == 0 + } + + public func toArray()-> Array { + var r = Array() + for i in self { + r.append(i) + } + return r + } +} + diff --git a/Tests/SwiftyCollectionTests/SwiftyCollectionTests.swift b/Tests/SwiftyCollectionTests/SwiftyCollectionTests.swift new file mode 100644 index 0000000..08cbbc1 --- /dev/null +++ b/Tests/SwiftyCollectionTests/SwiftyCollectionTests.swift @@ -0,0 +1,7 @@ +import XCTest +@testable import SwiftyCollection + +final class SwiftyCollectionTests: XCTestCase { + func testExample() throws { + } +}