I have a data structure that looks similiar to
[{:Gender "Boy" :Cat1 "Foo" :Cat2 "Bar" :SKU 111}
{:Gender "Boy" :Cat1 "Foo" :Cat2 "Bar" :SKU 222}
{:Gender "Girl" :Cat1 "Foo" :Cat2 "Bar" :SKU 333}
{:Gender "Boy" :Cat1 "Foo" :Cat2 "Woo" :SKU 444}]
Im wanting to build out a data structure that looks like
[{:Name "Boy"
:Children
{ :Name "Foo"
:Children
{:Name "Bar"
:Children
{:SKU 111}
{:SKU 222}
}
{:Name "Woo"
:Children
{:SKU 444}
}
}
{:Name "Girl"
:Children
{:Name "Foo"
:Children
{:Name "Bar"
:Children
{:SKU 333}
}
}
}]
Im fairly new to Clojure so if the answers is obvious please excuse me.
Below is the solution (NOTE: The :Children should be a list of map)
(def data [{:Gender "Boy" :Cat1 "Foo" :Cat2 "Bar" :SKU 111}
{:Gender "Boy" :Cat1 "Foo" :Cat2 "Bar" :SKU 222}
{:Gender "Girl" :Cat1 "Foo" :Cat2 "Bar" :SKU 333}
{:Gender "Boy" :Cat1 "Foo" :Cat2 "Woo" :SKU 444}])
(defn gp-by [data key]
(->> (group-by key data)
(map (fn [[k v]] {:Name k
:Children (map #(dissoc % key) v)}))))
(map (fn [m] (assoc m :Children
(map (fn [n] (assoc n :Children (gp-by (n :Children) :Cat2)))
(gp-by (m :Children) :Cat1)))) (gp-by data :Gender))
Not exactly what you want, but maybe close:
user=> (def foo [{:Gender "Boy" :Cat1 "Foo" :Cat2 "Bar" :SKU 111}
#_=> {:Gender "Boy" :Cat1 "Foo" :Cat2 "Bar" :SKU 222}
#_=> {:Gender "Girl" :Cat1 "Foo" :Cat2 "Bar" :SKU 333}
#_=> {:Gender "Boy" :Cat1 "Foo" :Cat2 "Woo" :SKU 444}])
#'user/foo
user=> (group-by (juxt :Gender :Cat1 :Cat2) foo)
{["Boy" "Foo" "Bar"] [{:Gender "Boy", :Cat2 "Bar", :Cat1 "Foo", :SKU 111}
{:Gender "Boy", :Cat2 "Bar", :Cat1 "Foo", :SKU 222}],
["Girl" "Foo" "Bar"] [{:Gender "Girl", :Cat2 "Bar", :Cat1 "Foo", :SKU 333}],
["Boy" "Foo" "Woo"] [{:Gender "Boy", :Cat2 "Woo", :Cat1 "Foo", :SKU 444}]}
user=> (def foo2 (group-by (juxt :Gender :Cat1 :Cat2) foo))
#'user/foo2
user=> (zipmap (keys foo2) (map #(map :SKU %) (vals foo2)))
{["Boy" "Foo" "Woo"] (444), ["Girl" "Foo" "Bar"] (333), ["Boy" "Foo" "Bar"] (111 222)}
Related
How would I sort
{
{:name "d" :id 2}
{:name "f" :id 3}
{:name "a" :id 1}
{:name "z" :id 9}
}
Alphabetically by name? Like this:
{
{:name "a" :id 1}
{:name "d" :id 2}
{:name "f" :id 3}
{:name "z" :id 9}
}
When in doubt, be sure to look at the Clojure CheatSheet.
In this case just use sort-by
(def data
[{:name "d" :id 2}
{:name "f" :id 3}
{:name "a" :id 1}
{:name "z" :id 9}])
(sort-by :name data) =>
({:name "a", :id 1}
{:name "d", :id 2}
{:name "f", :id 3}
{:name "z", :id 9})
Note that I had to fix your data to use square brackets [...]
After executing a query against the db, the return of the fuction is the list of maps:
({:id 1 :name "Book 1" :category "Drama"}
{:id 2 :name "Book 2" :category "Drama"}
{:id 3 :name "Book 3" :category "Poetry"}
{:id 4 :name "Book 4" :category "Poetry"}
{:id 5 :name "Book 5" :category "Fantasy"}
{:id 6 :name "Book 6" :category "Fantasy"}
{:id 7 :name "Book 7" :category "Fantasy"}
{:id 8 :name "Book 8" :category "Science fiction"}
{:id 9 :name "Book 9" :category "Science fiction"}
{:id 10 :name "Book 10" :category "Science fiction"}
...)
So, I group data by category and group-by function returns a persistent array-map contains strs keys and vector of maps as vals:
{"Fantasy" [{:category "Fantasy", :name "Book 5", :id 5}
{:category "Fantasy", :name "Book 6", :id 6}
{:category "Fantasy", :name "Book 7", :id 7}],
"Drama" [{:category "Drama", :name "Book 1", :id 1}
{:category "Drama", :name "Book 2", :id 2}],
"Poetry" [{:category "Poetry", :name "Book 3", :id 3}
{:category "Poetry", :name "Book 4", :id 4}],
"Science fiction" [{:category "Science fiction",
:name "Book 8",
:id 8}
{:category "Science fiction",
:name "Book 9",
:id 9}
{:category "Science fiction",
:name "Book 10",
:id 10}]}
Next, I do this:
(doseq [[k [{:keys [id name]} v]] data]
(println k)
(println id name))
The side-effect is:
Drama
1 Book1
Poetry
3 Book3
Fantasy
5 Book5
Science fiction
8 Book8
doseq returned only one value for each key.
How can I get the rest of the values?
The result must be:
Drama
1 Book1
2 Book2
Poetry
3 Book3
4 Book4
Fantasy
5 Book5
6 Book6
7 Book7
Science fiction
8 Book8
9 Book9
10 Book10
you can just make an inner loop like this:
(doseq [[k vs] data]
(println k)
(doseq [{:keys [id name]} vs]
(println id name)))
or using single doseq:
(doseq [[k vs] data
{:keys [id name] :as v} vs]
(when (= v (first vs))
(println k))
(println id name))
also there is one more dirty way to print outer loop string once:
(doseq [[k vs] data
:when (or (println k) true)
{:keys [id name] :as v} vs]
(println id name))
or even like this:
(doseq [[k vs] data
{:keys [id name] :as v} (do (println k) vs)]
(println id name))
I need to cut array after key in Ruby, for example:
=> ["Foo", "Bar", "Baz", "ooF", "raB", "zaB"] # Cut after "Baz"
=> ["Baz", "ooF", "raB", "zaB"] # result
It's possible to do? How can I do that?
Yes, just specify the range from index to -1(last element):
arr = ["Foo", "Bar", "Baz", "ooF", "raB", "zaB"]
arr[arr.index("Baz")..-1] # => ["Baz", "ooF", "raB", "zaB"]
Here is one more way to do this:
a = ["Foo", "Bar", "Baz", "ooF", "raB", "zaB"]
a.drop_while {|e| e != "Baz"}
#=> ["Baz", "ooF", "raB", "zaB"]
a.drop_while {|e| e != "Bazzzzzzz"}
#=> []
arr = ["Foo", "Bar", "Baz", "ooF", "raB", "zaB"]
arr.select { |s| s=='Baz'..nil ? 'Baz' : nil }
#=> ["Baz", "ooF", "raB", "zaB"]
Look odd? What's going on here?
Can anyone explain to me this result please:
(trad = {foo: "Foo", bar:"Bar"}).has_key? :foo ? trad[:foo] : :foo
=> false
I expected it to return:
=> "Foo"
(trad = {foo: "Foo", bar:"Bar"}).has_key? :foo ? trad[:foo] : :foo
is like:
(trad = {foo: "Foo", bar:"Bar"}).has_key? (:foo ? trad[:foo] : :foo)
:foo ? trad[:foo] : :foo is evaluated to "Foo" because :foo is treated as truth value.
(trad = {foo: "Foo", bar:"Bar"}).has_key? "Foo" yields false because there's no "Foo" key.
Use following (override precedence by surrounding parentheses) to get the expected result:
>> ((trad = {foo: "Foo", bar:"Bar"}).has_key? :foo) ? trad[:foo] : :foo
=> "Foo"
Hash#fetch(key, default) seems more appropriate:
>> {foo: "Foo", bar:"Bar"}.fetch(:foo, :foo)
=> "Foo"
>> {foo: "Foo", bar:"Bar"}.fetch(:baz, :baz)
=> :baz
For want of parenthesis the app was lost...
(trad = {foo: "Foo", bar:"Bar"}).has_key? :foo ? trad[:foo] : :foo # => false
(trad = {foo: "Foo", bar:"Bar"}).has_key?(:foo) ? trad[:foo] : :foo # => "Foo"
I'd be careful writing code like this:
(trad = {foo: "Foo", bar:"Bar"})
It's not idiomatic, so use:
trad = {foo: "Foo", bar:"Bar"}
trad.has_key?...
The reason is, in times of panic, like at 2:45 AM when your coding partner gets a call about a system outage because he's on call, and dives into the code, that assignment could be hard to find.
In a code review I'd suggest something like this over the other:
trad = {foo: "Foo", bar:"Bar"}
trad.has_key?(:foo) ? trad[:foo]
: :foo # => "Foo"
Note: This only works on Ruby 1.9+.
That all said, I'd highly recommend using fetch as recommended by #falsetru.
I am writing a ruby method that takes a string like this
"foo = { :foo => 'bar', :baz => \"{'foo' : 'bar', 'bar' : 'biff' }\" :bar => 'baz' }, bar, baz = \"('foo,bar,baz')\", &block"
and returns an array like this:
["foo = { :foo => 'bar', :baz => \"{'foo' : 'bar', 'bar' : 'biff' }\" :bar => 'baz' }", "bar", "baz = \"('foo,bar,baz')\"", "&block"]
However, so far I am unable to split the string correctly, my best effort still breaks the string on internal hashes e.g.
"foo = { :foo => 'bar', :baz => \"{'foo' : 'bar', 'bar' : 'biff' }\" :bar => 'baz' }, bar, "baz = \"('foo,bar,baz')\", &block".scan(/(?:[^,(]|\([^)]*\))+/)
Which produces:
["foo = { :foo => 'bar'", " :baz => \"{'foo' : 'bar'", " 'bar' : 'biff' }\" :bar => 'baz' }", " bar", " baz = \"('foo,bar,baz')\"", " &block"]
I think the regex i am using is close but i am not sure how to check for both parenthesis and curly brackets. Presently, the regex only searches for parentheses.
This is my current regex:
/(?:[^,(]|\([^)]*\))+/
Any help is greatly appreciated.
this does not suit you?
string.split(',')