Skip to content

Commit 95d8df5

Browse files
authored
Merge pull request #609 from simonwhitaker/master
Multiset
2 parents e3c9661 + 6c7eb0c commit 95d8df5

File tree

6 files changed

+203
-1
lines changed

6 files changed

+203
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//: Playground - noun: a place where people can play
2+
3+
#if swift(>=4.0)
4+
print("Hello, Swift 4!")
5+
#endif
6+
7+
import Cocoa
8+
9+
var set = Multiset<String>()
10+
11+
set.add("Foo")
12+
set.add("Foo")
13+
set.add("Bar")
14+
set.add("Baz")
15+
16+
set.count
17+
set.count(for: "Foo")
18+
19+
set.allItems
20+
21+
var set2 = Multiset<String>()
22+
set2.add("Foo")
23+
set2.add("Foo")
24+
25+
set2.isSubSet(of: set) // true
26+
set.isSubSet(of: set2) // false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//
2+
// Multiset.swift
3+
// Multiset
4+
//
5+
// Created by Simon Whitaker on 28/08/2017.
6+
//
7+
8+
import Foundation
9+
10+
public struct Multiset<Element: Hashable> {
11+
private var storage: [Element: UInt] = [:]
12+
public private(set) var count: UInt = 0
13+
14+
public init() {}
15+
16+
public mutating func add (_ elem: Element) {
17+
storage[elem, default: 0] += 1
18+
count += 1
19+
}
20+
21+
public mutating func remove (_ elem: Element) {
22+
if let currentCount = storage[elem] {
23+
if currentCount > 1 {
24+
storage[elem] = currentCount - 1
25+
} else {
26+
storage.removeValue(forKey: elem)
27+
}
28+
count -= 1
29+
}
30+
}
31+
32+
public func isSubSet (of superset: Multiset<Element>) -> Bool {
33+
for (key, count) in storage {
34+
let supersetcount = superset.storage[key] ?? 0
35+
if count > supersetcount {
36+
return false
37+
}
38+
}
39+
return true
40+
}
41+
42+
public func count(for key: Element) -> UInt {
43+
return storage[key] ?? 0
44+
}
45+
46+
public var allItems: [Element] {
47+
var result = [Element]()
48+
for (key, count) in storage {
49+
for _ in 0 ..< count {
50+
result.append(key)
51+
}
52+
}
53+
return result
54+
}
55+
}
56+
57+
// MARK: - Equatable
58+
extension Multiset: Equatable {
59+
public static func == (lhs: Multiset<Element>, rhs: Multiset<Element>) -> Bool {
60+
if lhs.storage.count != rhs.storage.count {
61+
return false
62+
}
63+
for (lkey, lcount) in lhs.storage {
64+
let rcount = rhs.storage[lkey] ?? 0
65+
if lcount != rcount {
66+
return false
67+
}
68+
}
69+
return true
70+
}
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='5.0' target-platform='macos'>
3+
<timeline fileName='timeline.xctimeline'/>
4+
</playground>

Multiset/README.markdown

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Multiset
2+
3+
A multiset (also known as a bag) is a data structure similar to a regular set, but it can store multiple instances of the same element.
4+
5+
The following all represent the same *set*, but do not represent the same *multiset*.
6+
7+
```
8+
[1, 3, 2]
9+
[1, 3, 2, 2]
10+
```
11+
12+
In this multiset implementation, ordering is unimportant. So these two multisets are identical:
13+
14+
```
15+
[1, 2, 2]
16+
[2, 1, 2]
17+
```
18+
19+
Typical operations on a multiset are:
20+
21+
- Add an element
22+
- Remove an element
23+
- Get the count for an element (the number of times it's been added)
24+
- Get the count for the whole set (the number of items that have been added)
25+
- Check whether it is a subset of another multiset
26+
27+
## Implementation
28+
29+
Under the hood, this implementation of Multiset uses a dictionary to store a mapping of elements to the number of times they've been added.
30+
31+
Here's the essence of it:
32+
33+
``` swift
34+
public struct Multiset<Element: Hashable> {
35+
fileprivate var storage: [Element: UInt]
36+
37+
public init() {
38+
storage = [:]
39+
}
40+
```
41+
42+
And here's how you'd use this class to create a multiset of strings:
43+
44+
``` swift
45+
var set = Multiset<String>()
46+
```
47+
48+
Adding an element is a case of incrementing the counter for that element, or setting it to 1 if it doesn't already exist:
49+
50+
``` swift
51+
public mutating func add (_ elem: Element) {
52+
if let currentCount = storage[elem] {
53+
storage[elem] = currentCount + 1;
54+
} else {
55+
storage[elem] = 1
56+
}
57+
}
58+
```
59+
60+
Here's how you'd use this method to add to the set we created earlier:
61+
62+
```swift
63+
set.add("foo")
64+
set.add("foo")
65+
```
66+
67+
Our set now contains two elements, both the string "foo".
68+
69+
Removing an element works much the same way as adding; decrement the counter for the element, or remove it from the underlying dictionary if its value is 1 before removal.
70+
71+
``` swift
72+
public mutating func remove (_ elem: Element) {
73+
if let currentCount = storage[elem] {
74+
if currentCount > 1 {
75+
storage[elem] = currentCount - 1
76+
} else {
77+
storage.removeValue(forKey: elem)
78+
}
79+
}
80+
}
81+
```
82+
83+
Getting the count for an item is simple: we just return the value for the given item in the internal dictionary.
84+
85+
``` swift
86+
public func count(for key: Element) -> UInt {
87+
return storage[key] ?? 0
88+
}
89+
```
90+
91+
*Written for the Swift Algorithm Club by Simon Whitaker*

README.markdown

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ Most of the time using just the built-in `Array`, `Dictionary`, and `Set` types
172172

173173
- [Bloom Filter](Bloom%20Filter/). A constant-memory data structure that probabilistically tests whether an element is in a set.
174174
- [Hash Set](Hash%20Set/). A set implemented using a hash table.
175-
- Multiset
175+
- [Multiset](Multiset/). A set where the number of times an element is added matters. (Also known as a bag.)
176176
- [Ordered Set](Ordered%20Set/). A set where the order of items matters.
177177

178178
### Graphs

swift-algorithm-club.xcworkspace/contents.xcworkspacedata

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)