Created
February 23, 2013 22:04
-
-
Save hcliff/5021557 to your computer and use it in GitHub Desktop.
Clojure methods in coffeescript for dealing with nested data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### | |
Walkers, used in the mixins walk, preWalk and postWalk | |
Note: against underscore argument convention due to | |
issues with _.partial | |
Shamelessly copied from bit.ly/X3tk8A | |
### | |
preWalk = (f, c)-> | |
_.walk f(c), _.partial(preWalk, f), _.identity | |
# Apply the function to the subform | |
postWalk = (f, c)-> | |
_.walk c, _.partial(postWalk, f), f | |
# Extend underscore | |
_.mixin | |
# Takes a property name string or | |
# an array of property strings | |
# or a function | |
lookupIterator: (v)-> | |
if _.isFunction(v) | |
return v | |
if _.isArray(v) | |
f = (k)->@[k] | |
return (o)-> _.map v, f, o | |
return (o)-> | |
return o[v] | |
# Given a object and an array of keys return nested items | |
# _.getIn({"one" : {"two" {"three" : "hi!"}}}, ['one', 'two']) | |
# -> {"three" : "hi!"} | |
getIn : (c, k)-> | |
reducer = (ret, x)-> | |
# If the reducer found no key path previously | |
# ret is undefined and thus will not have the key x | |
return ret[x] if _.isObject(ret) and _.has(ret, x) | |
undefined | |
_.reduce(k, reducer, c) | |
# Given an object, an array of keys and a value, set the value | |
# creating the key path as needed | |
setIn : (c, k, v)-> | |
reducer = (ret, x)-> | |
ret[x] = {} unless _.has(ret, x) | |
ret[x] | |
# Set up the key path by creating objects where needed | |
o = _.reduce(k.slice(0, -1), reducer, c) | |
# Assign the actual value to the last key | |
o[_.last(k)] = v | |
# apply a function f, to the value found at the keypath k in c | |
# any extra arguments passed onto f | |
updateIn : (c, k, f, fa...)-> | |
ov = _.getIn c, k | |
_.setIn c, k, f(ov, fa...) | |
c | |
# Implement a nested groupBy, takes a collection c and a | |
# array of arguments v | |
groupBy : (c, v) -> | |
f = _.lookupIterator(v || _.identity) | |
grouper = (o, v)-> | |
(if _.isUndefined(o) then (o = []) else o).push v | |
o | |
reducer = (ret, x)-> | |
_.updateIn(ret, f(x), grouper, x) | |
_.reduce(c, reducer, {}) | |
walk : (c, inner, outer)-> | |
# NOTE: arrays also are true for isObject | |
if _.isArray c | |
return outer _.map(c, inner) | |
if _.isObject c | |
return outer _.object(_.keys(c), _.map(c, inner)) | |
outer(c) | |
# Take arguments in the underscore idiom (collection then function) | |
postWalk : (c, f)-> | |
postWalk(f, c) | |
preWalk : (c, f)-> | |
preWalk(f, c) | |
collection = [ | |
{'n' : 1, 'v' : 'potato', 'c' : 'red'}, | |
{'n' : 1, 'v' : 'potato', 'c' : 'yellow'}, | |
{'n' : 2, 'v' : 'tomato', 'c' : 'purple'}, | |
{'n' : 2, 'v' : 'potato', 'c' : 'red'}, | |
] | |
console.time('group') | |
collection = _.groupBy(collection, ['n', 'v']) | |
console.timeEnd('group') | |
# trivial example | |
post = (value, key, list)-> | |
if _.isString(value) | |
return value.toUpperCase() | |
return value | |
console.time('walk') | |
collection = _.postWalk(collection, post) | |
console.timeEnd('walk') | |
#_.getIn(collection, ['1', 'potato']) | |
# _.setIn(collection, ['2', 'tomato'], "orange") | |
# _.updateIn(collection, ['2', 'tomato'], (s)-> s.toUpperCase()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment