Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to turn a list of objects into a keyed array/object?

I'm trying to write code using Ramda to produce a new data structure, using only the id and comment keys of the original objects. I'm new to Ramda and it's giving me some fits, although I have experience with what I think is similar coding with Python.

Given the following initial data structure…

const commentData = {
  '30': {'id': 6, 'comment': 'fubar', 'other': 7},
  '34': {'id': 8, 'comment': 'snafu', 'other': 6},
  '37': {'id': 9, 'comment': 'tarfu', 'other': 42}
};

I want to transform it into this…

{
  '6': 'fubar',
  '8': 'snafu',
  '9': 'tarfu'
}

I found the following example in the Ramda cookbook that comes close…

const objFromListWith = R.curry((fn, list) => R.chain(R.zipObj, R.map(fn))(list));
objFromListWith(R.prop('id'), R.values(commentData));

But values it returns includes the whole original object as the values…

{
  6: {id: 6, comment: "fubar", other: 7},
  8: {id: 8, comment: "snafu", other: 6},
  9: {id: 9, comment: "tarfu", other: 42}
}

How can I reduce the values down to the value of their comment key only?

I don't need to use the code I got from the cookbook. If anyone can suggest some code that will give the results I'm looking for that's also better (simpler, shorter, or more efficient) than the example here, I'll be happy to use that instead.

like image 758
Mr. Lance E Sloan Avatar asked Oct 08 '19 16:10

Mr. Lance E Sloan


2 Answers

If you don't mind, you don't need to use Ramda for that, pure JS can handle it nicely:

You can use a combination of Object.values(), to get all values of your first object (commentData) and .forEach() (or even .map(), but slower), in the array that results from Object.values to insert values into a new object dynamically.

const commentData = {
  '30': {'id': 6, 'comment': 'fubar', 'other': 7},
  '34': {'id': 8, 'comment': 'snafu', 'other': 6},
  '37': {'id': 9, 'comment': 'tarfu', 'other': 42}
};

let values = Object.values(commentData)
let finalObj = {};

values.forEach(x => finalObj[x.id] = x.comment)

console.log(finalObj)

But, if you want an one-liner, you can go with Object.fromEntries() after returning arrays of key/value from .map() based on id and comment, like below:

const commentData = {
  '30': {'id': 6, 'comment': 'fubar', 'other': 7},
  '34': {'id': 8, 'comment': 'snafu', 'other': 6},
  '37': {'id': 9, 'comment': 'tarfu', 'other': 42}
};

console.log(Object.fromEntries(Object.values(commentData).map(x => [x.id, x.comment])))
like image 90
Calvin Nunes Avatar answered Oct 08 '22 19:10

Calvin Nunes


A Ramda one-liner would be

const foo = compose(fromPairs, map(props(['id', 'comment'])), values)

const commentData = {
  '30': {'id': 6, 'comment': 'fubar', 'other': 7},
  '34': {'id': 8, 'comment': 'snafu', 'other': 6},
  '37': {'id': 9, 'comment': 'tarfu', 'other': 42}
}

console.log(
  foo(commentData)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {compose, fromPairs, map, props, values} = R           </script>

But that doesn't seem a lot cleaner than (a slight tweak to) the suggestion in epascarello's comment:

const foo = (obj) => 
  Object.values(obj).reduce((o, v) => (o[v.id] = v.comment) && o, {})

or a similar version I would write, if it didn't cause any performance issues:

const foo = (obj) => 
  Object.values(obj).reduce((o, {id, comment}) => ({...o, [id]: comment}), {})
like image 30
Scott Sauyet Avatar answered Oct 08 '22 18:10

Scott Sauyet