-
Notifications
You must be signed in to change notification settings - Fork 104
/
KeyedStorage.swift
92 lines (74 loc) · 2.28 KB
/
KeyedStorage.swift
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
//
// KeyedStorage.swift
// XMLCoder
//
// Created by Max Desiatov on 07/04/2019.
//
struct KeyedStorage<Key: Hashable & Comparable, Value> {
typealias Buffer = [(Key, Value)]
typealias KeyMap = [Key: [Int]]
fileprivate var keyMap = KeyMap()
fileprivate var buffer = Buffer()
var isEmpty: Bool {
return buffer.isEmpty
}
var count: Int {
return buffer.count
}
var keys: [Key] {
return buffer.map { $0.0 }
}
init<S>(_ sequence: S) where S: Sequence, S.Element == (Key, Value) {
buffer = Buffer()
keyMap = KeyMap()
sequence.forEach { key, value in append(value, at: key) }
}
subscript(key: Key) -> [Value] {
return keyMap[key]?.map { buffer[$0].1 } ?? []
}
mutating func append(_ value: Value, at key: Key) {
let i = buffer.count
buffer.append((key, value))
if keyMap[key] != nil {
keyMap[key]?.append(i)
} else {
keyMap[key] = [i]
}
}
func map<T>(_ transform: (Key, Value) throws -> T) rethrows -> [T] {
return try buffer.map(transform)
}
func compactMap<T>(
_ transform: ((Key, Value)) throws -> T?
) rethrows -> [T] {
return try buffer.compactMap(transform)
}
init() {}
}
extension KeyedStorage: Sequence {
func makeIterator() -> Buffer.Iterator {
return buffer.makeIterator()
}
}
extension KeyedStorage: CustomStringConvertible {
var description: String {
let result = buffer.map { "\"\($0)\": \($1)" }.joined(separator: ", ")
return "[\(result)]"
}
}
extension KeyedStorage where Key == String, Value == Box {
func merge(element: XMLCoderElement) -> KeyedStorage<String, Box> {
var result = self
let hasElements = !element.elements.isEmpty
let hasAttributes = !element.attributes.isEmpty
let hasText = element.stringValue != nil
if hasElements || hasAttributes {
result.append(element.transformToBoxTree(), at: element.key)
} else if hasText {
result.append(element.transformToBoxTree(), at: element.key)
} else {
result.append(SingleKeyedBox(key: element.key, element: NullBox()), at: element.key)
}
return result
}
}