Recommended way to collect elements into collection-valued Dictionary?


(Jens Persson) #1

let question = """
    Please see the extension to Dictionary below.
    Is there a simpler or better way to accomplish the same thing?
    """
extension Dictionary where
    Value: RangeReplaceableCollection,
    Value: ExpressibleByArrayLiteral,
    Value.Element == Value.ArrayLiteralElement
{
    mutating func append(value: Value.Element, for key: Key) {
        if self[key]?.append(value) == nil {
            self[key] = [value]
        }
    }
}
func usageExample() {
    var characterIndexMap = [Character : [Int]]()
    for (ci, c) in question.characters.enumerated() {
        characterIndexMap.append(value: ci, for: c)
    }
    for (character, indices) in characterIndexMap
        .sorted(by: { $0.1.count > $1.1.count })
    {
        print("\(character.debugDescription) occurs at indices:", indices)
    }
}
usageExample()

/* Will print:
" " occurs at indices: [6, 10, 14, 24, 27, 38, 48, 54, 56, 64, 67, 74, 78,
81, 92, 96, 101]
"e" occurs at indices: [2, 5, 8, 9, 13, 15, 18, 40, 51, 53, 62, 69, 72, 95,
100]
"t" occurs at indices: [11, 17, 25, 31, 49, 70, 71, 79, 93, 102]
"s" occurs at indices: [4, 7, 20, 47, 57, 90, 97]
"o" occurs at indices: [22, 26, 33, 42, 65, 80, 85]
"i" occurs at indices: [21, 29, 32, 58, 89, 104]
"a" occurs at indices: [3, 35, 55, 76, 82, 98]
"h" occurs at indices: [12, 50, 91, 94, 103]
"r" occurs at indices: [36, 52, 63, 66, 73]
"l" occurs at indices: [1, 41, 61, 88]
"n" occurs at indices: [19, 23, 34, 105]
"m" occurs at indices: [59, 86, 99]
"c" occurs at indices: [30, 83, 84]
"p" occurs at indices: [60, 87]
"w" occurs at indices: [43, 75]
"y" occurs at indices: [37, 77]
"b" occurs at indices: [39, 68]
"." occurs at indices: [44]
"\n" occurs at indices: [45]
"I" occurs at indices: [46]
"x" occurs at indices: [16]
"?" occurs at indices: [107]
"g" occurs at indices: [106]
"D" occurs at indices: [28]
"P" occurs at indices: [0]
*/


(Ben Cohen) #2

Hi – yes, there are a couple of things you can do:

You don’t have to rely on ExpressibleByArrayLiteral. RangeReplaceableCollection guarantees an empty init, so instead of [], you can write Value().

And the new 4.0 subscript that takes a default value allows you do avoid the if d[k] == nil dance for the initial value.

So you can write this as:

extension Dictionary where Value: RangeReplaceableCollection {
    mutating func append(value: Value.Element, for key: Key) {
        self[key, default: Value()].append(value)
    }
}

···

On Jun 15, 2017, at 3:12 PM, Jens Persson via swift-users <swift-users@swift.org> wrote:

let question = """
    Please see the extension to Dictionary below.
    Is there a simpler or better way to accomplish the same thing?
    """
extension Dictionary where
    Value: RangeReplaceableCollection,
    Value: ExpressibleByArrayLiteral,
    Value.Element == Value.ArrayLiteralElement
{
    mutating func append(value: Value.Element, for key: Key) {
        if self[key]?.append(value) == nil {
            self[key] = [value]
        }
    }
}
func usageExample() {
    var characterIndexMap = [Character : [Int]]()
    for (ci, c) in question.characters.enumerated() {
        characterIndexMap.append(value: ci, for: c)
    }
    for (character, indices) in characterIndexMap
        .sorted(by: { $0.1.count > $1.1.count })
    {
        print("\(character.debugDescription) occurs at indices:", indices)
    }
}
usageExample()

/* Will print:
" " occurs at indices: [6, 10, 14, 24, 27, 38, 48, 54, 56, 64, 67, 74, 78, 81, 92, 96, 101]
"e" occurs at indices: [2, 5, 8, 9, 13, 15, 18, 40, 51, 53, 62, 69, 72, 95, 100]
"t" occurs at indices: [11, 17, 25, 31, 49, 70, 71, 79, 93, 102]
"s" occurs at indices: [4, 7, 20, 47, 57, 90, 97]
"o" occurs at indices: [22, 26, 33, 42, 65, 80, 85]
"i" occurs at indices: [21, 29, 32, 58, 89, 104]
"a" occurs at indices: [3, 35, 55, 76, 82, 98]
"h" occurs at indices: [12, 50, 91, 94, 103]
"r" occurs at indices: [36, 52, 63, 66, 73]
"l" occurs at indices: [1, 41, 61, 88]
"n" occurs at indices: [19, 23, 34, 105]
"m" occurs at indices: [59, 86, 99]
"c" occurs at indices: [30, 83, 84]
"p" occurs at indices: [60, 87]
"w" occurs at indices: [43, 75]
"y" occurs at indices: [37, 77]
"b" occurs at indices: [39, 68]
"." occurs at indices: [44]
"\n" occurs at indices: [45]
"I" occurs at indices: [46]
"x" occurs at indices: [16]
"?" occurs at indices: [107]
"g" occurs at indices: [106]
"D" occurs at indices: [28]
"P" occurs at indices: [0]
*/

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Jens Persson) #3

Ah, exactly what I was looking for, thanks!

···

On Friday, June 16, 2017, Ben Cohen <ben_cohen@apple.com> wrote:

Hi – yes, there are a couple of things you can do:

You don’t have to rely on ExpressibleByArrayLiteral.
RangeReplaceableCollection guarantees an empty init, so instead of [], you
can write Value().

And the new 4.0 subscript that takes a default value allows you do avoid
the if d[k] == nil dance for the initial value.

So you can write this as:

extension Dictionary where Value: RangeReplaceableCollection {
    mutating func append(value: Value.Element, for key: Key) {
        self[key, default: Value()].append(value)
    }
}

On Jun 15, 2017, at 3:12 PM, Jens Persson via swift-users < > swift-users@swift.org > <javascript:_e(%7B%7D,'cvml','swift-users@swift.org');>> wrote:

let question = """
    Please see the extension to Dictionary below.
    Is there a simpler or better way to accomplish the same thing?
    """
extension Dictionary where
    Value: RangeReplaceableCollection,
    Value: ExpressibleByArrayLiteral,
    Value.Element == Value.ArrayLiteralElement
{
    mutating func append(value: Value.Element, for key: Key) {
        if self[key]?.append(value) == nil {
            self[key] = [value]
        }
    }
}
func usageExample() {
    var characterIndexMap = [Character : [Int]]()
    for (ci, c) in question.characters.enumerated() {
        characterIndexMap.append(value: ci, for: c)
    }
    for (character, indices) in characterIndexMap
        .sorted(by: { $0.1.count > $1.1.count })
    {
        print("\(character.debugDescription) occurs at indices:", indices)
    }
}
usageExample()

/* Will print:
" " occurs at indices: [6, 10, 14, 24, 27, 38, 48, 54, 56, 64, 67, 74, 78,
81, 92, 96, 101]
"e" occurs at indices: [2, 5, 8, 9, 13, 15, 18, 40, 51, 53, 62, 69, 72,
95, 100]
"t" occurs at indices: [11, 17, 25, 31, 49, 70, 71, 79, 93, 102]
"s" occurs at indices: [4, 7, 20, 47, 57, 90, 97]
"o" occurs at indices: [22, 26, 33, 42, 65, 80, 85]
"i" occurs at indices: [21, 29, 32, 58, 89, 104]
"a" occurs at indices: [3, 35, 55, 76, 82, 98]
"h" occurs at indices: [12, 50, 91, 94, 103]
"r" occurs at indices: [36, 52, 63, 66, 73]
"l" occurs at indices: [1, 41, 61, 88]
"n" occurs at indices: [19, 23, 34, 105]
"m" occurs at indices: [59, 86, 99]
"c" occurs at indices: [30, 83, 84]
"p" occurs at indices: [60, 87]
"w" occurs at indices: [43, 75]
"y" occurs at indices: [37, 77]
"b" occurs at indices: [39, 68]
"." occurs at indices: [44]
"\n" occurs at indices: [45]
"I" occurs at indices: [46]
"x" occurs at indices: [16]
"?" occurs at indices: [107]
"g" occurs at indices: [106]
"D" occurs at indices: [28]
"P" occurs at indices: [0]
*/

_______________________________________________
swift-users mailing list
swift-users@swift.org
<javascript:_e(%7B%7D,'cvml','swift-users@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-users