Skip to content

Commit

Permalink
Merge pull request #15 from DandyLyons/docs-bijective
Browse files Browse the repository at this point in the history
Add Docs
  • Loading branch information
ladvoc authored Sep 12, 2024
2 parents 0521c33 + 657b3a9 commit 5af52d6
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 0 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Creating a BijectiveDictionary

Learn the many different ways to initialize a `BijectiveDictionary`.

## Creating an Empty `BijectiveDictionary`
An empty dictionary can be created using ``BijectiveDictionary/init()``
Because `BijectiveDictionary` is generic, you will also need to somewhere declare your `Left` and `Right` value types.

```swift
let bDict = BijectiveDictionary<String, Int>()
```

Or an empty `BijectiveDictionary` can also be created with a literal. However, this will require that you explicitly declare the type (unless it can be inferred elsewhere).

```swift
let bDict: BijectiveDictionary<String, Int> = [:]
```

### Reserving Capacity
An empty dictionary can also be created with a minimum capacity using ``BijectiveDictionary/init(minimumCapacity:)``. Remember that, (just like a standard `Dictionary`) when a `BijectiveDictionary` exceeds it's capacity, it must reallocate its storage buffer under the hood. This reallocation is automatic and you do not need to remember to do it, but it is not free. It is good to avoid this work, if you know in advance that you will have a large number of items.

```swift
let bDict = BijectiveDictionary<String, Int>(minimumCapacity: 10_000)
```

## Initialize with Values
### Create a `BijectiveDictionary` from a Literal
See: ``BijectiveDictionary/init(dictionaryLiteral:)``

``BijectiveDictionary`` conforms to `ExpressibleByDictionaryLiteral` and therefore can be initialized with common `Dictionary` syntax. (Note, this creates a `BijectiveDictionary` directly, and does not create any `Dictionary`.) In practice, this is one of the easiest ways to create a `BijectiveDictionary`.

># Warning
>The dictionary literal used here must not contain any duplicates in the left or right values or else this initializer will fatal error.
```swift
let bDict: BijectiveDictionary<String, Int> = ["A": 1, "B": 2, "C": 3]
```


### Initialize with a Sequence of Tuples
See ``BijectiveDictionary/init(uniqueLeftRightPairs:)``
```swift
let values = [
("A", 1),
("B", 2),
("C", 3),
]
let bDict = BijectiveDictionary(uniqueLeftRightPairs: values)
```

## Conversion
### Convert a `Dictionary` to a `BijectiveDictionary`
See ``BijectiveDictionary/init(_:)``

```swift
let dict = ["A": 1, "B": 2, "C": 3]
guard let bDict = BijectiveDictionary(dict) else {
print("Right values are non-unique.")
}
print("bDict is now unwrapped.")
```

### Convert a `BijectiveDictionary` to a `Dictionary`
```swift
let bDict: BijectiveDictionary = ["A": 1, "B": 2, "C": 3]
let dict = Dictionary(bDict)
```

### Codable
`BijectiveDictionary` also conforms to `Codable` and therefore can be easily converted back and forth from `JSON`, `Data`, `XML` and any other serialization scheme that supports `Codable`. The `Codable` implementation for `BijectiveDictionary` is designed to perfectly mimic `Dictionary`'s `Codable` implementation. In other words, you should expect that and encoded representation of a `Dictionary` should produce an equivalent `BijectiveDictionary` and vice versa.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Using a BijectiveDictionary
Learn how to mutate and maintain a BijectiveDictionary.

## Reserving Capacity
Just like a `Dictionary`, if a ``BijectiveDictionary`` overflows its capacity, then it must internally allocate for more memory. While this process is automatic, it is not free. If we know the required capacity ahead of time, then we can allocate it in advance, thus eliminating unnecessary work. This can be done during initialization.

```swift
let bDict = BijectiveDictionary<String, Int>(minimumCapacity: 10_000)
```

Or it can also be done during the lifetime of the dictionary.

```swift
print(bDict.capacity) // 10_000
bDict.reserveCapacity(200_000)
print(bDict.capacity) // 200_000
```
## Reading a Value
```swift
let bDict: BijectiveDictionary = ["A": 1, "B": 2, "C": 3]
bDict[left: "B"] // 2
bDict[right: 3] // "C"
```

## Order
># `BijectiveDictionary` does not preserve or guarantee ordering.
>Do not forget that just like `Dictionary`, a `BijectiveDictionary` is unordered. The order of key-value pairs is stable between mutations but is otherwise unpredictable. For more info, see the docs for [Dictionary](https://github.com/swiftlang/swift/blob/28c6cc105da2e917416002df3e0accfc2ad2d23f/stdlib/public/core/Dictionary.swift#L316).
For the purposes of these docs, we will preserve the order, just to make it easier to reason about. But in practice, in actual code, the order of elements in a `BijectiveDictionary` will not be predictable.

## Mutation
Insertion and updates are done through the subscripts. The syntax is just like mutating a `Dictionary`. However, unlike a `Dictionary` both the "key" and the "value" can be used as a key. This is the reason why we instead use the words "left" and "right". We must also add these words to our subscript when we call them. In other words we must use `bDict[left: "B"]` rather than `bDict["B"]`.

### Insertion
Just like a `Dictionary`, if the given "key" is not found, then the new key-value pair will be inserted.

```swift
var bDict: BijectiveDictionary = ["A": 1, "B": 2, "C": 3]
bDict[left: "D"] = 4
bDict[right: 5] = "E"
print(bDict) // ["A": 1, "B": 2, "C": 3, "D": 4, "E": 5]
```

Swift will also check for types.
```swift
var bDict: BijectiveDictionary = ["A": 1, "B": 2, "C": 3]
bDict[left: 4] = "D" // this is a compiler error
```

### Updates
Also just like a `Dictionary`, if the given "key" already exists, then the subscript will keep the "key" as-is, and will mutate the corresponding "value".

```swift
var bDict: BijectiveDictionary = ["A": 1, "B": 2, "C": 3]
bDict[left: "A"] = 12
bDict[right: 2] = "Z"
print(bDict) // ["A": 12, "Z": 2, "C": 3]
```


### Removal
Removal can be performed by using ``BijectiveDictionary/remove(byLeft:)`` and ``BijectiveDictionary/remove(byRight:)`` respectively.

```swift
var bDict = ["A": 1, "B": 2, "C": 3, "D": 4, "E": 5]
bDict.remove(byLeft: "D")
print(bDict) // ["A": 1, "B": 2, "C": 3, "E": 5]
bDict.remove(byRight: 5)
print(bDict) // ["A": 1, "B": 2, "C": 3]
```


Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# What Are Duplicates?
Learn what `BijectiveDictionary` means when referring to duplicates.

## Duplicates
Throughout the documentation we will use the term `duplicate`. This term has a slightly different meaning in `BijectiveDictionary` than it means in other collections such as `Dictionary`.

### Duplicates in `Dictionary`
A `Dictionary` cannot have duplicate keys, but it can have duplicate values. Therefore when you create a `Dictionary` using the initializer `Dictionary.init(uniqueKeysWithValues:)`, you must provide unique keys. In other words the keys can have no duplicates. But it is okay for the values to have duplicates. Providing duplicate keys will result in a runtime error.

For this reason, `Dictionary` also comes with another initializer (`Dictionary.init(_:uniquingKeysWith:)`). This initializer will not result in a runtime error. In order to achieve this, you must provide it a closure which will be called any time that duplicate keys are encountered. This will instruct the initializer how to resolve the duplicate.

### Duplicates in `BijectiveDictionary`
`BijectiveDictionary` aims to mimic `Dictionary`'s API as closely as possible, only making changes when necessary. Unlike `Dictionary`, a `BijectiveDictionary` requires both the left and the right values to be unique. But let's be a little more specific on what that means.

1. Within the left values, there can be no duplicates.
2. Within the right values, there can be no duplicates.
3. However, it is okay if the same value exists on both the right and left side. This is not considered a duplicate.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# What does 'Bijective' mean?

Learn how bijection affects the behavior of `BijectiveDictionary`.

## Overview
[Bijection](https://en.wikipedia.org/wiki/Bijection) is the property of having one-to-one correspondence between two sets.

![](Bijection.svg)

From this definition we can see that ``BijectiveDictionary`` has these important characteristics:
1. Every left value is **unique** (just like a `Dictionary` key).
2. Every right value is **also unique** (unlike a `Dictionary` value).
3. Every left value has **one and only one** corresponding right value.
4. Every right value has **one and only one** corresponding left value.

Just like a `Set`, all key-value pairs in a `BijectiveDictionary` are unique and unordered.

If you do not want these properties for your particular use case, then `BijectiveDictionary` is not the right tool for the job.

0 comments on commit 5af52d6

Please sign in to comment.