<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div><br class=""><blockquote type="cite" class=""><div class=""><div class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><ul class="" style="box-sizing: border-box; padding-left: 2em; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; background-color: rgb(255, 255, 255);"><li class="" style="box-sizing: border-box; margin-top: 0.25em;">Add <code class="" style="box-sizing: border-box; font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 11.600000381469727px; padding: 0.2em 0px; margin: 0px; background-color: rgba(0, 0, 0, 0.0392157); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">Dictionary.mapValues</code> to return a <code class="" style="box-sizing: border-box; font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 11.600000381469727px; padding: 0.2em 0px; margin: 0px; background-color: rgba(0, 0, 0, 0.0392157); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">Dictionary</code> (can be more efficiently implemented than composition as the storage layout remains the same).</li></ul></div></div></blockquote><div class="">+1000. I have also been asking for this since the beginning. I built my own version (and use it frequently), but as you say, the standard library can do it much more efficiently.</div><div class=""><br class=""></div><div class="">I would also like to see an in-place version as well.</div><div class=""><br class=""></div><div class="">One design detail. Even though it is only mapping the values, I would like it to pass the key to the closure as well. It occasionally figures in to the mapping logic.</div></div></div></div></blockquote><div class=""><br class=""></div>Do you have any examples you can share where you use the key in this type of map? I'm not contesting its usefulness; it would just be helpful to see some real-world usage.<br class=""></div></div></div></blockquote><div><br class=""></div><div>I use mapValues mostly for simple transformations. Calling .lowercased on Strings so that they are consistent or lightening/darkening colors. </div><div><br class=""></div><div>Where the key comes in is when the key is actually gives context to the value (i.e. the value only makes sense in the context of the key). This isn’t the prettiest example, but I have some animation code where I store some values defining the animation details in a dictionary keyed by the view to be animated. Then I can run down each view and check if it needs setup/etc… based on those details. If I need to update those details based on the current state of the view, I need access to the associated view in map. Embarrassingly messy. I would most likely refactor before releasing the code publicly, but it does work.</div><div><br class=""></div><div>I would be ok with a simpler mapValues as long as a version that can change the key is also available somewhere. That would cover any of my use cases (and I would just keep the key the same).</div><div><br class=""></div><div>I actually almost use the key transform more often (though I still like mapValues for it’s simplicity when that is what is needed). I transform string keys to be uniform (lowercased & removing diacritics). For example I have a parser which has a built-in color keyword that can match color names and push the associated color on the stack. The programmer can set a [String:UIColor] dictionary of names/colors. I run through and normalize the names so that the normalized user input will match.</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div class=""><div class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><p class="" style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; background-color: rgb(255, 255, 255);">Please reply here with any comments or questions on the above list, or any additions you believe are important that are missing from it.</p></div></div></blockquote><div class="">I would also like to see a version of map which returns a dictionary and handles key collisions:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>let newDict = myDict.map(collision: {k,v1,v2 in v2}) { (k,v) in ... }</div><div class=""><br class=""></div><div class="">The collision parameter would take a throwing closure and handle the case of a key conflict (by returning the value to use, throwing, or trapping). It would have a default value so that it would only have to be specified if a different behavior was desired. </div><div class=""><br class=""></div><div class="">In advanced cases, the collision could be used to accumulate values together. Because of this, I would actually like to see this on *collection* (not just dictionary). The map closure is handed each element of the sequence (which in the case of dictionary is a key/value tuple), and expects a return value of a key/value tuple. The collision block is called when a key is returned which has already been used to figure out what value to use. This might choose a winner, or it could act like reduce, building a value from the components.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">I think the uses you're describing are handled by the merging initializer proposed in SE-100:</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0100-add-sequence-based-init-and-merge-to-dictionary.md" class="">https://github.com/apple/swift-evolution/blob/master/proposals/0100-add-sequence-based-init-and-merge-to-dictionary.md</a></div><div class=""><br class=""></div><div class="">For example, you can use a combining closure to select specific elements when the keys collide:</div><div class=""><br class=""></div></div></div><blockquote class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; margin: 0px 0px 0px 40px; border: none; padding: 0px;"><div class=""><div class=""><div class="">let duplicates: DictionaryLiteral = ["a": 1, "b": 2, "a": 3, "b": 4]</div></div></div><div class=""><div class=""><div class="">// Using the first value only</div></div></div><div class=""><div class=""><div class="">Dictionary(merging: duplicates, combine: { (first, _) in first }) // ["b": 2, "a": 1]</div></div></div><div class=""><div class=""><div class="">// Using the maximum value</div></div></div><div class=""><div class=""><div class="">Dictionary(merging: duplicates, combine: max) // ["b": 4, "a": 3]</div></div></div></blockquote><div class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div class=""><div class=""><br class=""></div><div class="">or to calculate the frequencies of values in a sequence:</div><div class=""><br class=""></div></div></div><blockquote class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; margin: 0px 0px 0px 40px; border: none; padding: 0px;"><div class=""><div class=""><div class="">extension Sequence where Iterator.Element: Hashable {</div></div></div><div class=""><div class=""><div class=""><div class=""> func frequencies() -> [Iterator.Element: Int] {</div></div></div></div><div class=""><div class=""><div class=""><div class=""> return Dictionary(merging: self.lazy.map { v in (v, 1) }, combine: +)</div></div></div></div><div class=""><div class=""><div class=""><div class=""> }</div></div></div></div><div class=""><div class=""><div class=""><div class="">}</div></div></div></div><div class=""><div class=""><div class=""><div class="">[1, 2, 2, 3, 1, 2, 4, 5, 3, 2, 3, 1].frequencies()</div></div></div></div><div class=""><div class=""><div class="">// [2: 4, 4: 1, 5: 1, 3: 3, 1: 3]</div></div></div></blockquote><div class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div class=""><div class=""><br class=""></div><div class="">Could you take a look and see if that provides the functionality you're looking for?</div></div></div></div></blockquote><div><br class=""></div><div>Almost. From the document, the signature was slightly different. There it only works on sequences of tuples (i.e. dictionaries). If the signature was changed to work with any sequence (as you show above), then it would meet my needs.</div><div><br class=""></div><div>I do kind of like being able to call it as a method on sequence (the same way you call map on sequence), but that is just a matter of style. You could always build one form from the other...</div><div><br class=""></div><div>Thanks,</div><div>Jon</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div class=""></div></div></blockquote></div><br class=""></body></html>