Skip to content

Instantly share code, notes, and snippets.

@jem-computer
Last active November 12, 2015 20:26
Show Gist options
  • Save jem-computer/88530028115fb4136d95 to your computer and use it in GitHub Desktop.
Save jem-computer/88530028115fb4136d95 to your computer and use it in GitHub Desktop.

hope this makes sense. I had a list of objects like this:

const xs = [
  { foo: 2, bar: 2, baz: 3 },
  { qux: 1, bar: 0.5 },
  { foo: 3, bar: 2, qux: 4 },
]

that I wanted to turn into a single object, by passing a function to merge similar keys/values.

Not every key is present in every object.

I can't use mergeAll(xs) because that would just overwrite values; what I want to do here is supply a function for how they should be combined.

The example in the documentation block ^^^ shows how I'm initially using it - with add (but it could be used with any function)

It seems like this is still overly complicated - two things I'm thinking:

  • zipWith does almost exactly what I want to do, but it works on lists rather than objects
  • there might be trickier Ramda concepts that I'm missing out on. I still don't understand lift, for example. Am I missing something?
import { expect } from 'chai'
import { mergeWith } from '../src/utils'
import { curry, mapObjIndexed, merge, propOr, reduce } from 'ramda'
/**
* takes a function and two objects
* merges them by applying fn to
* the same k/v in each.
*
* @func
* @sig (a,a -> a) -> {k: v} -> {k: v} -> {k: v}
* @param {Function} fn A function used to merge items
* @param {Object} obj1 The first object
* @param {Object} obj2 The first object
* @return {Object} The result of applying fn to each val in obj1 & obj2
* @example
*
* const fn = R.add
* const obj1 = { foo: 1, bar: 10, baz: 5 }
* const obj2 = { foo: 8, bar: 20, baz: 7 }
* mergeWith(fn, obj1, obj2)
* //=> { foo: 9, bar: 30, baz: 12 }
*/
export const mergeWith = curry((fn, fallback, a, b) => {
return merge(a, mapObjIndexed((vb, k) => {
const va = propOr(0, k, a)
return fn(va, vb)
}, b))
})
describe('mergeWith', () => {
it('merges two lists with a supplied function', () => {
const fn1 = (a, b) => a + b
const fn2 = (a, b) => a * b
const fallback = 0
const obj1 = { foo: 1, bar: 10, baz: 5 }
const obj2 = { foo: 8, bar: 20, baz: 7 }
const expected1 = { foo: 9, bar: 30, baz: 12 }
const expected2 = { foo: 8, bar: 200, baz: 35 }
expect(mergeWith(fn1, fallback, obj1, obj2)).to.eql(expected1)
expect(mergeWith(fn2, fallback, obj1, obj2)).to.eql(expected2)
})
it('plays nicely with reduce', () => {
const fn1 = (a, b) => a + b
const fallback = 0
const obj1 = { foo: 1, bar: 10, baz: 5 }
const obj2 = { foo: 8, bar: 20, baz: 7 }
const obj3 = { foo: 5, bar: 50, baz: 9 }
const expected = { foo: 14, bar: 80, baz: 21 }
expect(reduce(mergeWith(fn1, fallback), {}, [obj1, obj2, obj3])).to.eql(expected)
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment