constructing maps
Possibly I'm going about this wrong. I'm trying to understand how best to construct maps from sequences, by applying a function which returns a key / value pair. Something like this: (ns test (:use clojure.contrib.str-utils)) (def test-str "foo=1;bar=2;baz=3") (defn split-kv [text] (let [[k v] (re-split #"=" text )] {k v})) (defn split-pairs [text] (re-split #";" text)) (map split-kv (split-pairs test-str)) -> ({"foo" "1"} {"bar" "2"} {"baz" "3"}) Doesn't really do what I had in mind. And yeah, I figured out that I can convert that to a hash-map in various ways, but I had expected map to be able to do this. So I wrote this: (defn map-assoc "Returns a map consisting of f applied to coll, where f is a function returning a key and value. f can return either a sequence with two values or a single item map." [f coll] (loop [map {} s (seq coll)] (if s (let [item (f (first s))] (recur (if (associative? item) (conj map item) (assoc map (first item) (second item))) (next s))) map))) This seems a little bit more like what I expected: (map-assoc split-kv (split-pairs test-str)) -> {"baz" "3", "bar" "2", "foo" "1"} Am I overlooking some already existing function hidden away someplace that does this? --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
On May 4, 2009, at 14:23, Nathan Hawkins wrote: > This seems a little bit more like what I expected: > > (map-assoc split-kv (split-pairs test-str)) > > -> {"baz" "3", "bar" "2", "foo" "1"} > > > Am I overlooking some already existing function hidden away someplace > that does this? Here's one way to do what you want: (ns test (:use clojure.contrib.str-utils)) (def test-str "foo=1;bar=2;baz=3") (defn split-kv [text] (vec (re-split #"=" text))) (defn split-pairs [text] (re-split #";" text)) (apply conj {} (map split-kv (split-pairs test-str))) Konrad. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
On May 4, 7:23 am, Nathan Hawkins wrote: > Possibly I'm going about this wrong. I'm trying to understand how best > to construct maps from sequences, by applying a function which returns a > key / value pair. > > Something like this: > > (ns test (:use clojure.contrib.str-utils)) > > (def test-str "foo=1;bar=2;baz=3") > > (defn split-kv [text] > (let [[k v] (re-split #"=" text )] > {k v})) > > (defn split-pairs [text] > (re-split #";" text)) > > (map split-kv (split-pairs test-str)) > > -> ({"foo" "1"} {"bar" "2"} {"baz" "3"}) > > Doesn't really do what I had in mind. And yeah, I figured out that I can > convert that to a hash-map in various ways, but I had expected map to be > able to do this. So I wrote this: > > (defn map-assoc > "Returns a map consisting of f applied to coll, where f is a function > returning a key and value. f can return either a sequence with two > values or a single item map." > [f coll] > (loop [map {} > s (seq coll)] > (if s > (let [item (f (first s))] > (recur (if (associative? item) > (conj map item) > (assoc map (first item) (second item))) > (next s))) > map))) > > This seems a little bit more like what I expected: > > (map-assoc split-kv (split-pairs test-str)) > > -> {"baz" "3", "bar" "2", "foo" "1"} > > Am I overlooking some already existing function hidden away someplace > that does this? user> (apply hash-map (clojure.contrib.str-utils/re-split #"[=;]" "foo=1;bar=2;baz=3")) {"foo" "1", "bar" "2", "baz" "3"} This solution makes a lot of assumptions about the input data, of course. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
Nathan Hawkins wrote: > Possibly I'm going about this wrong. I'm trying to understand how > best to construct maps from sequences, by applying a function which > returns a key / value pair. [...] > Am I overlooking some already existing function hidden away someplace > that does this? Here's a more succinct way: user> (let [test-str "foo=1;bar=2;baz=3"] (reduce conj {} (map #(apply hash-map (seq (.split % "="))) (.split test-str ";" {"baz" "3", "bar" "2", "foo" "1"} -Drew --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
Nathan Hawkins a écrit : > Ok, my example seems to have misled. You're missing the point a little > bit: > > 1. I was trying to avoid the (reduce conj {} ...), by having the map > function do it. Why even build a list that's only going to get thrown > away when I want a hash-map at the end? > > 2. The functions used to split the strings were not important, only an > example. It could just as easily be a function to extract fields from a > java object. > > > To some extent, I guess I'm thinking in terms of Common Lisp, where I'd > build an a-list with mapcar and cons. > With f a function that return a [key value] pair (or a (key value) pair but not a {key value} pair): (reduce #(apply assoc %1 (f %2)) {} coll) if you want to have f return a map you can (reduce #(merge %1 (f %2)) {} coll) -- Professional: http://cgrand.net/ (fr) On Clojure: http://clj-me.blogspot.com/ (en) --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
2009/5/4 Nathan Hawkins : > > Possibly I'm going about this wrong. I'm trying to understand how best > to construct maps from sequences, by applying a function which returns a > key / value pair. > > Something like this: > > (ns test (:use clojure.contrib.str-utils)) > > (def test-str "foo=1;bar=2;baz=3") > > (defn split-kv [text] > (let [[k v] (re-split #"=" text )] > {k v})) > > (defn split-pairs [text] > (re-split #";" text)) > > (map split-kv (split-pairs test-str)) > > -> ({"foo" "1"} {"bar" "2"} {"baz" "3"}) > > > Doesn't really do what I had in mind. And yeah, I figured out that I can > convert that to a hash-map in various ways, but I had expected map to be > able to do this. Hi, no, maybe you make a confusion with the word "map" as in "hash-map" and as in "mapping". The latter is the meaning for the map function: mapping a sequence to another sequence (and the result will always be a sequence). --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
On Mon, 4 May 2009 16:07:06 +0200 Christopher Taylor wrote: > > Hi Nathan, > > On 04.05.2009, at 15:47, Nathan Hawkins wrote: > > > > > On Mon, 4 May 2009 06:16:14 -0700 (PDT) > > Drew Raines wrote: > >> > >> Whoops, that (seq) is a debugging artifact. You can remove that: > >> > >> (let [test-str "foo=1;bar=2;baz=3"] > >> (reduce conj {} > >> (map #(apply hash-map (.split % "=")) > >> (.split test-str ";" > > > > Ok, my example seems to have misled. You're missing the point a > > little bit: > > > > 1. I was trying to avoid the (reduce conj {} ...), by having the map > > function do it. Why even build a list that's only going to get > > thrown away when I want a hash-map at the end? > > you're not actually building a list. The function map returns a > (lazy) *sequence*, which is an instance of ISeq. This just means > you're getting something that supports the operations first and rest. > So, since map returns a sequence and you want a Map (i.e. key/value > data structure), you'll have to turn it into one by using (conj > {} ...) or (into {} ...). There's the source of my misunderstanding. I knew map returned a sequence, but hadn't quite connected "lazy" to the problem at hand. I had stumbled on a couple different ways of converting the sequence to a hash-map (I found (into {} ...), (apply hash-map ...) and (reduce conj {} ...)), but I was thinking in terms of how I'd solve the problem in Common Lisp so I thought that running map and then converting the result to a hash-map was going to iterate the list twice, as well as cons the results twice. Thanks for clearing that up. Nathan --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
On Mon, 4 May 2009 06:16:14 -0700 (PDT) Drew Raines wrote: > > On May 4, 8:05 am, Drew Raines wrote: > > > user> (let [test-str "foo=1;bar=2;baz=3"] > > (reduce conj {} > > (map #(apply hash-map (seq (.split % "="))) > > (.split test-str ";" > > Whoops, that (seq) is a debugging artifact. You can remove that: > > (let [test-str "foo=1;bar=2;baz=3"] > (reduce conj {} > (map #(apply hash-map (.split % "=")) >(.split test-str ";" Ok, my example seems to have misled. You're missing the point a little bit: 1. I was trying to avoid the (reduce conj {} ...), by having the map function do it. Why even build a list that's only going to get thrown away when I want a hash-map at the end? 2. The functions used to split the strings were not important, only an example. It could just as easily be a function to extract fields from a java object. To some extent, I guess I'm thinking in terms of Common Lisp, where I'd build an a-list with mapcar and cons. Nathan --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
Hi Nathan, On 04.05.2009, at 15:47, Nathan Hawkins wrote: > > On Mon, 4 May 2009 06:16:14 -0700 (PDT) > Drew Raines wrote: >> >> Whoops, that (seq) is a debugging artifact. You can remove that: >> >> (let [test-str "foo=1;bar=2;baz=3"] >> (reduce conj {} >> (map #(apply hash-map (.split % "=")) >> (.split test-str ";" > > Ok, my example seems to have misled. You're missing the point a little > bit: > > 1. I was trying to avoid the (reduce conj {} ...), by having the map > function do it. Why even build a list that's only going to get thrown > away when I want a hash-map at the end? you're not actually building a list. The function map returns a (lazy) *sequence*, which is an instance of ISeq. This just means you're getting something that supports the operations first and rest. So, since map returns a sequence and you want a Map (i.e. key/value data structure), you'll have to turn it into one by using (conj {} ...) or (into {} ...). hth, --Chris --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
On May 4, 8:05 am, Drew Raines wrote: > user> (let [test-str "foo=1;bar=2;baz=3"] > (reduce conj {} > (map #(apply hash-map (seq (.split % "="))) > (.split test-str ";" Whoops, that (seq) is a debugging artifact. You can remove that: (let [test-str "foo=1;bar=2;baz=3"] (reduce conj {} (map #(apply hash-map (.split % "=")) (.split test-str ";" -Drew --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
On Mon, 04 May 2009 16:31:21 +0200 Christophe Grand wrote: > > Nathan Hawkins a écrit : > > Ok, my example seems to have misled. You're missing the point a > > little bit: > > > > 1. I was trying to avoid the (reduce conj {} ...), by having the map > > function do it. Why even build a list that's only going to get > > thrown away when I want a hash-map at the end? > > > > 2. The functions used to split the strings were not important, only > > an example. It could just as easily be a function to extract fields > > from a java object. > > > > > > To some extent, I guess I'm thinking in terms of Common Lisp, where > > I'd build an a-list with mapcar and cons. > > > > With f a function that return a [key value] pair (or a (key value) > pair but not a {key value} pair): > (reduce #(apply assoc %1 (f %2)) {} coll) > > if you want to have f return a map you can > (reduce #(merge %1 (f %2)) {} coll) > This is exactly what I was trying to, but I hadn't thought of using reduce. Thank you. Nathan --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
On May 4, 9:03 am, Konrad Hinsen wrote: > On May 4, 2009, at 14:23, Nathan Hawkins wrote: > > > This seems a little bit more like what I expected: > > > (map-assoc split-kv (split-pairs test-str)) > > > -> {"baz" "3", "bar" "2", "foo" "1"} > > > Am I overlooking some already existing function hidden away someplace > > that does this? > > Here's one way to do what you want: > > (ns test (:use clojure.contrib.str-utils)) > > (def test-str "foo=1;bar=2;baz=3") > > (defn split-kv [text] > (vec (re-split #"=" text))) > > (defn split-pairs [text] > (re-split #";" text)) > > (apply conj {} (map split-kv (split-pairs test-str))) > The last can be simplified by using into: (into {} (map split-kv (split-pairs test-str))) -- Michel --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
Michel S. a écrit : >> (apply conj {} (map split-kv (split-pairs test-str))) >> >> > The last can be simplified by using into: > (into {} (map split-kv (split-pairs test-str))) > It should be noted that into (and conj) is somewhat tricky with maps: user=> (into {} '({1 2} {3 4})) {3 4, 1 2} user=> (into {} '([1 2] [3 4])) {3 4, 1 2} user=> (into {} '((1 2) (3 4))) java.lang.ClassCastException: java.lang.Integer cannot be cast to java.util.Map$Entry (NO_SOURCE_FILE:0) It bit me before. Christophe -- Professional: http://cgrand.net/ (fr) On Clojure: http://clj-me.blogspot.com/ (en) --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
On May 4, 5:07 pm, Christophe Grand wrote: > Michel S. a écrit : > > >> (apply conj {} (map split-kv (split-pairs test-str))) > > > The last can be simplified by using into: > > (into {} (map split-kv (split-pairs test-str))) > > It should be noted that into (and conj) is somewhat tricky with maps: > user=> (into {} '({1 2} {3 4})) > {3 4, 1 2} > user=> (into {} '([1 2] [3 4])) > {3 4, 1 2} > user=> (into {} '((1 2) (3 4))) > java.lang.ClassCastException: java.lang.Integer cannot be cast to > java.util.Map$Entry (NO_SOURCE_FILE:0) > > It bit me before. > Yes, me too. The design is a bit unfortunate, as I cannot convert a Scheme-style association list to a map easily: user=> (into {} '((cars bmw chevrolet ford peugeot) (genres adventure horror mystery))) java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.util.Map$Entry (NO_SOURCE_FILE:0) -- Michel --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
(into {} (apply map vector '((cars bmw chevrolet ford peugeot) (genres adventure horror mystery {ford mystery, chevrolet horror, bmw adventure, cars genres} On Mon, May 4, 2009 at 4:03 PM, Michel S. wrote: > > > > On May 4, 5:07 pm, Christophe Grand wrote: >> Michel S. a écrit : >> >> >> (apply conj {} (map split-kv (split-pairs test-str))) >> >> > The last can be simplified by using into: >> > (into {} (map split-kv (split-pairs test-str))) >> >> It should be noted that into (and conj) is somewhat tricky with maps: >> user=> (into {} '({1 2} {3 4})) >> {3 4, 1 2} >> user=> (into {} '([1 2] [3 4])) >> {3 4, 1 2} >> user=> (into {} '((1 2) (3 4))) >> java.lang.ClassCastException: java.lang.Integer cannot be cast to >> java.util.Map$Entry (NO_SOURCE_FILE:0) >> >> It bit me before. >> > Yes, me too. The design is a bit unfortunate, as I cannot convert a > Scheme-style association list to a map easily: > > user=> (into {} '((cars bmw chevrolet ford peugeot) > (genres adventure horror mystery))) > > java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to > java.util.Map$Entry (NO_SOURCE_FILE:0) > > -- > Michel > > > -- And what is good, Phaedrus, And what is not good— Need we ask anyone to tell us these things? --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
Kevin Downey a écrit : > (into {} (apply map vector > '((cars bmw chevrolet ford peugeot) > (genres adventure horror mystery > > {ford mystery, chevrolet horror, bmw adventure, cars genres} > or: user=> (apply zipmap '((cars bmw chevrolet ford peugeot) (genres adventure horror mystery))) {ford mystery, chevrolet horror, bmw adventure, cars genres} But I'm unsure it's what Michel was after. I thought an alist was a list of pointed pairs (or a list of 2-elts lists) but I may be wrong since I come from Javaland. user=> (into {} (map vec '((a 1) (b 2) (c 3 {c 3, b 2, a 1} Christophe > > On Mon, May 4, 2009 at 4:03 PM, Michel S. wrote: > >> >> Yes, me too. The design is a bit unfortunate, as I cannot convert a >> Scheme-style association list to a map easily: >> >> user=> (into {} '((cars bmw chevrolet ford peugeot) >> (genres adventure horror mystery))) >> >> java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to >> java.util.Map$Entry (NO_SOURCE_FILE:0) >> >> -- >> Michel >> -- Professional: http://cgrand.net/ (fr) On Clojure: http://clj-me.blogspot.com/ (en) --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---
Re: constructing maps
On Tue, 05 May 2009 09:39:21 +0200 Christophe Grand wrote: > > Kevin Downey a écrit : > > (into {} (apply map vector > > '((cars bmw chevrolet ford peugeot) > > (genres adventure horror mystery > > > > {ford mystery, chevrolet horror, bmw adventure, cars genres} > > > > or: > user=> (apply zipmap '((cars bmw chevrolet ford peugeot) (genres > adventure horror mystery))) > > {ford mystery, chevrolet horror, bmw adventure, cars genres} > > But I'm unsure it's what Michel was after. I thought an alist was a > list of pointed pairs (or a list of 2-elts lists) but I may be wrong > since I come from Javaland. > > user=> (into {} (map vec '((a 1) (b 2) (c 3 > {c 3, b 2, a 1} > > > Christophe > > > > On Mon, May 4, 2009 at 4:03 PM, Michel S. > > wrote: > >> > >> Yes, me too. The design is a bit unfortunate, as I cannot convert a > >> Scheme-style association list to a map easily: > >> > >> user=> (into {} '((cars bmw chevrolet ford peugeot) > >> (genres adventure horror mystery))) > >> > >> java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to > >> java.util.Map$Entry (NO_SOURCE_FILE:0) > >> > >> -- > >> Michel > >> > > In Common Lisp, an alist is something like this: ((cars . genres) (bmw . adventure) (chevrolet . horror) (ford . mystery)) Where (cars . genres) is a cons cell with values in both slots. So an alist is a list of key/value pairs where order is significant and keys could appear more than once. Not much different than: [[cars genres] [bmw adventure] [chevrolet horror] [ford mystery]] --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~--~~~~--~~--~--~---