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?
> 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.
> 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
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
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).
On 04.05.2009, at 15:47, Nathan Hawkins wrote:
>
> On Mon, 4 May 2009 06:16:14 -0700 (PDT)
> Drew Raines <aara...@gmail.com> 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
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
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)
This is exactly what I was trying to, but I hadn't thought of using
reduce.
Thank you.
Nathan
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
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. <michel...@gmail.com> 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]]