5

What's an elegent way - purely functional, ideally - to transform (reduce?) this array:

var in = [
  { a: 1, b: 'x', c: 'foo' },
  { a: 1, b: 'y', c: 'goo' },
  { a: 2, b: 'x', c: 'hoo' },
  { a: 2, b: 'y', c: 'joo' }
]

Into this:

var out = [
  { a: 1, x: 'foo', y: 'goo' },
  { a: 2, x: 'hoo', y: 'joo' }
]

The logic is that all elements should be joined based on their a property, and all b and c properties denote key/value pairs respectively that should be merged into the single object based on their shared a value.

3
  • you want the last element in the set of all elements with the same a value? Commented Feb 12, 2017 at 18:40
  • What is logic to derive out? Note, in throws Uncaught SyntaxError: Unexpected token in Commented Feb 12, 2017 at 18:40
  • I described the logic in my question, sorry for not stating that upfront. Commented Feb 12, 2017 at 18:43

5 Answers 5

8

You can use a hash object, and reduce to wrap the hashing like this:

const arr = [
  { a: 1, b: 'x', c: 'foo' },
  { a: 1, b: 'y', c: 'goo' },
  { a: 2, b: 'x', c: 'hoo' },
  { a: 2, b: 'y', c: 'joo' }
];

let result = Object.values(             // the result is the values of the hash object
  arr.reduce((hash, o) => {             // hash is a hash object that make it easier to group the result
    hash[o.a] = hash[o.a] || {a: o.a};  // if there is no object in the hash that have the value of the key a equal to o.a, then create a new one
    hash[o.a][o.b] = o.c;               // set the value of the key stored in o.b to o.c
    return hash;
  }, {})
);

console.log(result);

Sign up to request clarification or add additional context in comments.

4 Comments

forEach really isn't purely functional. The reduce is the way to go.
@Bergi I've been hesitating. I'll remove the first and keep the second. It's more elegant and that's what OP is asking for!
Now for that you'll get a +1 :D
Just a note on Object.values compatability, it's ES2017 so before using it make sure it's supported in your environments. Great answer otherwise.
5

You could use a closure with a Map

var input = [{ a: 1, b: 'x', c: 'foo' }, { a: 1, b: 'y', c: 'goo' }, { a: 2, b: 'x', c: 'hoo' }, { a: 2, b: 'y', c: 'joo' }],
    output = input.reduce((map => (r, o) => (!map.has(o.a) && map.set(o.a, r[r.push({ a: o.a }) - 1]), map.get(o.a)[o.b] = o.c, r))(new Map), []);

console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

2

You can use forEach and Object.assign to group by a and then map to return object values.

var data = [
  { a: 1, b: 'x', c: 'foo' },
  { a: 1, b: 'y', c: 'goo' },
  { a: 2, b: 'x', c: 'hoo' },
  { a: 2, b: 'y', c: 'joo' }
]

var r = {}
data.forEach(e => r[e.a] = Object.assign((r[e.a] || {}), {a: e.a, [e.b]: e.c}))
r = Object.keys(r).map(e => r[e])

console.log(r)

1 Comment

And note that Object.assign((r[e.a] || {}), {a: e.a, [e.b]: e.c}) can be slightly shortened to {...(r[e.a] || {}), ...{a: e.a, [e.b]: e.c}} using object spread notation (and possibly Babel if necessary).
0

I like provided answers, but here is my attempt. I believe it's more readable, but it uses Object.assign and Object.values

const input = [
  { a: 1, b: 'x', c: 'foo' },
  { a: 1, b: 'y', c: 'goo' },
  { a: 2, b: 'x', c: 'hoo' },
  { a: 2, b: 'y', c: 'joo' }
]

const map = input.reduce((acc, obj) => {
  const [a, key, value] = Object.values(obj)
  const newObj = {a, [key]: value}

  if (acc[a]) {
    Object.assign(acc[a], newObj)
  } else {
   acc[a] = newObj
  }

  return acc 
}, {})

console.log(Object.values(map))

Comments

0

Not sure if approach is elegant or functional, though returns expected result using for..of loops, Array.prototype.some() and Object.assign()

function props(array, key, prop1, prop2) {
  let arr = [];
  for (let obj of array) {
    let o = {};
    for (let {[key]:_key, [prop1]:_prop1, [prop2]:_prop2} of [obj]) {
      o[_prop1] = _prop2;
      o[key] = _key;
    }
    if (!arr.some(p => p[key] === o[key])) arr.push(o);
    for (let prop of arr) {
      if (prop[key] == o[key]) {
        prop = Object.assign(prop, o)
      }
    }
  }
  return arr
}

var _in = [
  { a: 1, b: 'x', c: 'foo' },
  { a: 1, b: 'y', c: 'goo' },
  { a: 2, b: 'x', c: 'hoo' },
  { a: 2, b: 'y', c: 'joo' }
];

console.log(props(_in, "a", "b", "c"));

1 Comment

No, that's not functional at all. And for (let {[key]:_key, [prop1]:_prop1, [prop2]:_prop2} of [obj]) { is just insane.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.