Possibly less  computer science-y then you had in mind, but take a look at
Prismatic's Schema https://github.com/Prismatic/schema Graph:
https://github.com/Prismatic/plumbing#graph-the-functional-swiss-army-knife
and Fnhouse: https://github.com/Prismatic/fnhouse

Your mentioning of handling nulls brings to mind the maybe monad as well.
https://en.wikipedia.org/wiki/Monad_(functional_programming)#The_Maybe_monad

And I also like this handy little utility function:

(defn get->> [data pattern]
  (reduce #(%2 %1) data pattern))

(defn refract [data pattern]
  (if (map? pattern)
    (into {} (map #(vector (first %) (get->> data (second %))) pattern))
    (into [] (map #(get->> data (if (vector? %) % (vector %))) pattern))))

Given data like you have above, and a vector, or map of vectors, of fns,
return the results of the fns to the data.

- Jordan


On Tue, Jul 7, 2015 at 2:28 PM, Joe Graham <josgr...@gmail.com> wrote:

> Hi Clojure Users,
> I wanted to learn an idiomatic approach to data transformation in Clojure.
> For example I have a web service that is consuming some ReST services,
>  Ultimately I need to consolidate these services into a data model which
> resembles a DAG.  However I would like to transform the children of this
> data model in discrete transformation functions that may share some logic
> and the transforming functions should be able to handle nulls and fail or
> exit gracefully.  I have provided a simple example below.  I would
> appreciate any links to existing code and especially any links that include
> background or theory on the matter.  Bonus points if some examples use
> functors or monads or other interesting concepts.
>
> Here's an example, using the two input sources, consolidate into a new
> data model.  In the output json I have highlighted some common
> transformation points such as dates and currencies.  My understanding is
> that a pure function take on this would be just a function per mapping
> pair.  So for any given item in the output set map a function to the items
> in the input set.  But what other clever tricks can be applied and what
> would this look like?
>
> Thanks so much for your help on this!
>
> Input data (json) for apples:
> {
> "fruit-category" : "apple",
> "fruit-category-id": "2001",
>  "fruit-items": [
>    {
>     "fruit-item-id": "2001-1",
>     "description": "Fuji",
>     "current-price": "3.01",
>     "currency": "USD",
>    "fresh-until": "2015/07/16"
>   },
>   {
>     "fruit-item-id": "2001-2",
>     "description": "Honeycrisp",
>     "current-price": "1.69",
>     "currency": "USD",
>     "fresh-until": "2015/07/17"
>  }]
> }
>
>
> Input data (json) for oranges:
> {
> "fruit-category" : "orange",
> "fruit-category-id": "2002",
>  "fruit-items": [
>    {
>     "fruit-item-id": "2002-1",
>     "description": "Mandarin",
>     "current-price": "1.88",
>     "currency": "USD",
>    "fresh-until": "2015/07/19"
>   },
>   {
>     "fruit-item-id": "2002-2",
>     "description": "Clementine",
>     "current-price": "1.98",
>     "currency": "USD",
>     "fresh-until": "2015/07/13"
>  }]
> }
>
> Consolidated output data (json):
> {
>  "fruit-basket": {
>     "title": "Apples and Oranges",
>     "price": "17.12",
>     "currency": "USD",
>     "converted-currency":"JPY",
>     "converted-price":"2098.22",
>     "fresh-until":"2015/07/13",
>  },
>  "items": [{
>     "fruit-item-id":"2001-1",
>     "qty": "2",
>     }, {
>     "fruit-item-id":"2001-2",
>     "qty": "2",
>     }, {
>     "fruit-item-id":"2002-1",
>     "qty": "2",
>     }, {
>     "fruit-item-id":"2002-1",
>     "qty": "2",
>    }],
>   "entities": [
>    {
>     "fruit-item-id": "2001-1",
>     "description": "Fuji",
>     "current-price": "3.01",
>     "currency": "USD",
>    "fresh-until": "2015/07/16"
>   },
>   {
>     "fruit-item-id": "2001-2",
>     "description": "Honeycrisp",
>     "current-price": "1.69",
>     "currency": "USD",
>     "fresh-until": "2015/07/17"
>  },
>  {
>     "fruit-item-id": "2002-1",
>     "description": "Mandarin",
>     "current-price": "1.88",
>     "currency": "USD",
>    "fresh-until": "2015/07/19"
>   },
>   {
>     "fruit-item-id": "2002-2",
>     "description": "Clementine",
>     "current-price": "1.98",
>     "currency": "USD",
>     "fresh-until": "2015/07/13"
>  }
>   ],
>   "extra-stuff": {
>     "cache-key": "foo",
>     "other-data": "bar",
>    "timestamp-utc": "..."
>  }
> }
>
> --
> 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
> Note that posts from new members are moderated - please be patient with
> your first post.
> 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
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>

-- 
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
Note that posts from new members are moderated - please be patient with your 
first post.
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
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to