Skip to content

Instantly share code, notes, and snippets.

@natecook1000
Last active December 9, 2015 18:43
Show Gist options
  • Save natecook1000/92d495d9b95e35ee77db to your computer and use it in GitHub Desktop.
Save natecook1000/92d495d9b95e35ee77db to your computer and use it in GitHub Desktop.
Initializers for Dictionary like NSDictionary +dictionaryWithObjects:forKeys:
// (c) 2015 Nate Cook, licensed under the MIT license
//
// Initializers for Dictionary like NSDictionary +dictionaryWithObjects:forKeys:
public extension Dictionary {
/// Creates a new Dictionary from the given `keys` and `values` collections.
///
/// More efficient than the sequence version because it can reserve the correct
/// capacity before filling the dictionary. Returns `nil` if the lengths of the
/// two collections differ.
init?<ValueCollection: CollectionType, KeyCollection: CollectionType
where ValueCollection.Generator.Element == Value, KeyCollection.Generator.Element == Key>
(values: ValueCollection, forKeys keys: KeyCollection)
{
// Return nil if the two collections aren't the same length.
guard values.count == numericCast(keys.count)
else { return nil }
self = Dictionary(minimumCapacity: numericCast(keys.count))
for (key, value) in zip(keys, values) {
self[key] = value
}
}
/// Creates a new Dictionary from the given `keys`
/// and `values` sequences.
///
/// Returns `nil` if the lengths of the two sequences differ.
init?<ValueSequence: SequenceType, KeySequence: SequenceType
where ValueSequence.Generator.Element == Value, KeySequence.Generator.Element == Key>
(values: ValueSequence, forKeys keys: KeySequence)
{
self = Dictionary()
// If we use zip with sequences, we won't know if there's a
// length mismatch, so we need to use their generators manually.
var keyGenerator = keys.generate()
var valueGenerator = values.generate()
while true {
// If we use optional binding here, we won't be able to
// distinguish the case where both run out together (i.e.,
// the same length) from where one runs out first.
let key = keyGenerator.next()
let value = valueGenerator.next()
switch (key, value) {
// If key and value both bind, we can add the pair to
// the dictionary.
case let (.Some(key), .Some(value)):
self[key] = value
// If the two sequences run out at the same time, they
// were the same length: return cleanly.
case (.None, .None):
return
// Otherwise, one sequence bound and the other didn't,
// so return nil.
default:
return nil
}
}
}
}
let keys = ["one", "two", "three"]
let values = [1, 2, 3]
let moreValues = values + [4]
// these succeed
let dict1 = Dictionary(values: values, forKeys: keys)
let dict2 = Dictionary(values: AnySequence(values), forKeys: keys)
// these fail, since moreValues.count > keys.count
let dict3 = Dictionary(values: moreValues, forKeys: keys)
let dict4 = Dictionary(values: AnySequence(moreValues), forKeys: keys)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment