<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&nbsp;<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>&nbsp;to return a&nbsp;<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>&nbsp;(can be more efficiently implemented than composition as the storage layout remains the same).</li></ul></div></div></blockquote><div class="">+1000. &nbsp;I have also been asking for this since the beginning. &nbsp;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. &nbsp;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. &nbsp;</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). &nbsp;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. &nbsp;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. &nbsp;Embarrassingly messy. &nbsp;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). &nbsp;I transform string keys to be uniform (lowercased &amp; removing diacritics). &nbsp;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. &nbsp;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). &nbsp;It would have a default value so that it would only have to be specified if a different behavior was desired. &nbsp;</div><div class=""><br class=""></div><div class="">In advanced cases, the collision could be used to accumulate values together. &nbsp;Because of this, I would actually like to see this on *collection* (not just dictionary). &nbsp;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. &nbsp;The collision block is called when a key is returned which has already been used to figure out what value to use. &nbsp;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&nbsp;duplicates:&nbsp;DictionaryLiteral&nbsp;=&nbsp;["a":&nbsp;1,&nbsp;"b":&nbsp;2,&nbsp;"a":&nbsp;3,&nbsp;"b":&nbsp;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,&nbsp;combine: { (first,&nbsp;_)&nbsp;in first }) &nbsp; &nbsp;//&nbsp;["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,&nbsp;combine: max) &nbsp; &nbsp; &nbsp;//&nbsp;["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&nbsp;Sequence&nbsp;where&nbsp;Iterator.Element:&nbsp;Hashable {</div></div></div><div class=""><div class=""><div class=""><div class="">&nbsp; &nbsp; func&nbsp;frequencies()&nbsp;-&gt;&nbsp;[Iterator.Element:&nbsp;Int] {</div></div></div></div><div class=""><div class=""><div class=""><div class="">&nbsp; &nbsp; &nbsp; &nbsp; return&nbsp;Dictionary(merging:&nbsp;self.lazy.map&nbsp;{ v&nbsp;in&nbsp;(v,&nbsp;1) },&nbsp;combine:&nbsp;+)</div></div></div></div><div class=""><div class=""><div class=""><div class="">&nbsp; &nbsp; }</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,&nbsp;2,&nbsp;2,&nbsp;3,&nbsp;1,&nbsp;2,&nbsp;4,&nbsp;5,&nbsp;3,&nbsp;2,&nbsp;3,&nbsp;1].frequencies()</div></div></div></div><div class=""><div class=""><div class="">//&nbsp;[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. &nbsp;From the document, the signature was slightly different. &nbsp;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>