File: Multidictionary.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (146 lines) | stat: -rw-r--r-- 5,793 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//===------------------------- Multidictionary.swift ----------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020-2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

/// A collection that associates keys with one or more values.
@_spi(Testing) public struct Multidictionary<Key: Hashable, Value: Hashable>: Collection, Equatable {
  private var forwardDictionary: Dictionary<Key, Set<Value>> = [:]

  /// Reverse index used to make value removal more efficient.
  private var reverseIndex: Dictionary<Value, Set<Key>> = [:]

  public typealias Element = (Key, Set<Value>)
  public typealias Index = Dictionary<Key, Set<Value>>.Index

  public init() {}

  /// The number of key-value pairs in this multi-dictionary.
  public var count: Int {
    self.forwardDictionary.count
  }

  /// The position of the first element in a nonempty multi-dictionary.
  public var startIndex: Index {
    self.forwardDictionary.startIndex
  }

  /// The collection’s “past the end” position—that is, the position one greater
  /// than the last valid subscript argument.
  public var endIndex: Index {
    self.forwardDictionary.endIndex
  }

  /// Returns the index for the given key.
  ///
  /// - Parameter key: The key to find in the multi-dictionary.
  /// - Returns: The index for key and its associated value if key is in the
  ///            multi-dictionary; otherwise, nil.
  public func index(forKey key: Key) -> Dictionary<Key, Set<Value>>.Index? {
    self.forwardDictionary.index(forKey: key)
  }

  /// Computes the position immediately after the given index.
  ///
  /// - Parameter i: A valid index of the collection. i must be less than endIndex.
  /// - Returns: The position immediately after the given index.
  @_spi(Testing) public func index(after i: Dictionary<Key, Set<Value>>.Index) -> Dictionary<Key, Set<Value>>.Index {
    self.forwardDictionary.index(after: i)
  }

  @_spi(Testing) public subscript(position: Dictionary<Key, Set<Value>>.Index) -> (Key, Set<Value>) {
    self.forwardDictionary[position]
  }

  /// A collection containing just the keys of this multi-dictionary.
  public var keys: Dictionary<Key, Set<Value>>.Keys {
    return self.forwardDictionary.keys
  }

  /// A collection containing just the values of this multi-dictionary.
  public var values: Dictionary<Key, Set<Value>>.Values {
    return self.forwardDictionary.values
  }

  /// Accesses the values associated with the given key for reading and writing.
  public subscript(key: Key) -> Set<Value>? {
    self.forwardDictionary[key]
  }

  /// Accesses the values associated with the given key for reading and writing.
  ///
  /// If this multi-dictionary doesn’t contain the given key, accesses the
  /// provided default value as if the key and default value existed in
  /// this multi-dictionary.
  public subscript(key: Key, default defaultValues: @autoclosure () -> Set<Value>) -> Set<Value> {
    self.forwardDictionary[key, default: defaultValues()]
  }

  /// Returns a set of keys that the given value is associated with.
  ///
  /// - Parameter v: The value to search for among the key-value associations in
  ///                this dictionary.
  /// - Returns: The set of keys associated with the given value.
  public func keysContainingValue(_ v: Value) -> Set<Key> {
    return self.reverseIndex[v] ?? []
  }

  /// Inserts the given value in the set of values associated with the given key.
  ///
  /// - Parameters:
  ///   - v: The value to insert.
  ///   - key: The key used to associate the given value with a set of elements.
  /// - Returns: `true` if the value was not previously associated with any
  ///            other values for the given key. Else, returns `false.
  @discardableResult
  public mutating func insertValue(_ v: Value, forKey key: Key) -> Bool {
    let inserted1 = self.reverseIndex[v, default: []].insert(key).inserted
    let inserted2 = self.forwardDictionary[key, default: []].insert(v).inserted
    assert(inserted1 == inserted2)
    return inserted1
  }

  /// Removes the given value from the set of values associated with the given key.
  ///
  /// - Parameters:
  ///   - v: The value to remove.
  ///   - key: The key used to associate the given value with a set of elements.
  /// - Returns: The removed element, if any.
  @discardableResult
  public mutating func removeValue(_ v: Value, forKey key: Key) -> Value? {
    let removedKey = self.reverseIndex[v]?.remove(key)
    let removedVal = self.forwardDictionary[key]?.remove(v)
    assert((removedKey == nil && removedVal == nil) ||
           (removedKey != nil && removedVal != nil))
    return removedVal
  }

  /// Removes all occurrences of the given value from all entries in this
  /// multi-dictionary.
  ///
  /// - Note: If this value is used as a key, this function does not erase its
  ///         entries from the underlying dictionary.
  ///
  /// - Parameter v: The value to remove.
  public mutating func removeOccurrences(of v: Value) {
    for k in self.reverseIndex.removeValue(forKey: v) ?? [] {
      self.forwardDictionary[k]!.remove(v)
    }
    assert(expensivelyCheckThatValueIsRemoved(v))
  }

  /// For assertions. Returns true `v` is removed from all `forwardDictionary` values.
  private func expensivelyCheckThatValueIsRemoved(_ v: Value) -> Bool {
    if self.reverseIndex[v] != nil {
      return false
    }
    return !self.forwardDictionary.values.contains(where: { $0.contains(v) })
  }
}