Bag.swift 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. //
  2. // Bag.swift
  3. // ReactiveSwift
  4. //
  5. // Created by Justin Spahr-Summers on 2014-07-10.
  6. // Copyright (c) 2014 GitHub. All rights reserved.
  7. //
  8. /// A uniquely identifying token for removing a value that was inserted into a
  9. /// Bag.
  10. public final class RemovalToken {}
  11. /// An unordered, non-unique collection of values of type `Element`.
  12. public struct Bag<Element> {
  13. fileprivate var elements: ContiguousArray<BagElement<Element>> = []
  14. public init() {}
  15. /// Insert the given value into `self`, and return a token that can
  16. /// later be passed to `remove(using:)`.
  17. ///
  18. /// - parameters:
  19. /// - value: A value that will be inserted.
  20. @discardableResult
  21. public mutating func insert(_ value: Element) -> RemovalToken {
  22. let token = RemovalToken()
  23. let element = BagElement(value: value, token: token)
  24. elements.append(element)
  25. return token
  26. }
  27. /// Remove a value, given the token returned from `insert()`.
  28. ///
  29. /// - note: If the value has already been removed, nothing happens.
  30. ///
  31. /// - parameters:
  32. /// - token: A token returned from a call to `insert()`.
  33. public mutating func remove(using token: RemovalToken) {
  34. let tokenIdentifier = ObjectIdentifier(token)
  35. // Removal is more likely for recent objects than old ones.
  36. for i in elements.indices.reversed() {
  37. if ObjectIdentifier(elements[i].token) == tokenIdentifier {
  38. elements.remove(at: i)
  39. break
  40. }
  41. }
  42. }
  43. }
  44. extension Bag: Collection {
  45. public typealias Index = Array<Element>.Index
  46. public var startIndex: Index {
  47. return elements.startIndex
  48. }
  49. public var endIndex: Index {
  50. return elements.endIndex
  51. }
  52. public subscript(index: Index) -> Element {
  53. return elements[index].value
  54. }
  55. public func index(after i: Index) -> Index {
  56. return i + 1
  57. }
  58. public func makeIterator() -> BagIterator<Element> {
  59. return BagIterator(elements)
  60. }
  61. }
  62. private struct BagElement<Value> {
  63. let value: Value
  64. let token: RemovalToken
  65. }
  66. extension BagElement: CustomStringConvertible {
  67. var description: String {
  68. return "BagElement(\(value))"
  69. }
  70. }
  71. /// An iterator of `Bag`.
  72. public struct BagIterator<Element>: IteratorProtocol {
  73. private let base: ContiguousArray<BagElement<Element>>
  74. private var nextIndex: Int
  75. private let endIndex: Int
  76. fileprivate init(_ base: ContiguousArray<BagElement<Element>>) {
  77. self.base = base
  78. nextIndex = base.startIndex
  79. endIndex = base.endIndex
  80. }
  81. public mutating func next() -> Element? {
  82. let currentIndex = nextIndex
  83. if currentIndex < endIndex {
  84. nextIndex = currentIndex + 1
  85. return base[currentIndex].value
  86. }
  87. return nil
  88. }
  89. }