transform object to array with lodash - javascript

How can I transform a big object to array with lodash?
var obj = {
22: {name:"John", id:22, friends:[5,31,55], works:{books:[], films:[],}
12: {name:"Ivan", id:12, friends:[2,44,12], works:{books:[], films:[],}
}
// transform to
var arr = [{name:"John", id:22...},{name:"Ivan", id:12...}]

You can do
var arr = _.values(obj);
For documentation see here.

A modern native solution if anyone is interested:
const arr = Object.keys(obj).map(key => ({ key, value: obj[key] }));
or (not IE):
const arr = Object.entries(obj).map(([key, value]) => ({ key, value }));

_.toArray(obj);
Outputs as:
[
{
"name": "Ivan",
"id": 12,
"friends": [
2,
44,
12
],
"works": {
"books": [],
"films": []
}
},
{
"name": "John",
"id": 22,
"friends": [
5,
31,
55
],
"works": {
"books": [],
"films": []
}
}
]"

For me, this worked:
_.map(_.toPairs(data), d => _.fromPairs([d]));
It turns
{"a":"b", "c":"d", "e":"f"}
into
[{"a":"b"}, {"c":"d"}, {"e":"f"}]

There are quite a few ways to get the result you are after. Lets break them in categories:
ES6 Values only:
Main method for this is Object.values. But using Object.keys and Array.map you could as well get to the expected result:
Object.values(obj)
Object.keys(obj).map(k => obj[k])
var obj = {
A: {
name: "John"
},
B: {
name: "Ivan"
}
}
console.log('Object.values:', Object.values(obj))
console.log('Object.keys:', Object.keys(obj).map(k => obj[k]))
ES6 Key & Value:
Using map and ES6 dynamic/computed properties and destructuring you can retain the key and return an object from the map.
Object.keys(obj).map(k => ({[k]: obj[k]}))
Object.entries(obj).map(([k,v]) => ({[k]:v}))
var obj = {
A: {
name: "John"
},
B: {
name: "Ivan"
}
}
console.log('Object.keys:', Object.keys(obj).map(k => ({
[k]: obj[k]
})))
console.log('Object.entries:', Object.entries(obj).map(([k, v]) => ({
[k]: v
})))
Lodash Values only:
The method designed for this is _.values however there are "shortcuts" like _.map and the utility method _.toArray which would also return an array containing only the values from the object. You could also _.map though the _.keys and get the values from the object by using the obj[key] notation.
Note: _.map when passed an object would use its baseMap handler which is basically forEach on the object properties.
_.values(obj)
_.map(obj)
_.toArray(obj)
_.map(_.keys(obj), k => obj[k])
var obj = {
A: {
name: "John"
},
B: {
name: "Ivan"
}
}
console.log('values:', _.values(obj))
console.log('map:', _.map(obj))
console.log('toArray:', _.toArray(obj))
console.log('keys:', _.map(_.keys(obj), k => obj[k]))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Lodash Key & Value:
// Outputs an array with [[KEY, VALUE]]
_.entries(obj)
_.toPairs(obj)
// Outputs array with objects containing the keys and values
_.map(_.entries(obj), ([k,v]) => ({[k]:v}))
_.map(_.keys(obj), k => ({[k]: obj[k]}))
_.transform(obj, (r,c,k) => r.push({[k]:c}), [])
_.reduce(obj, (r,c,k) => (r.push({[k]:c}), r), [])
var obj = {
A: {
name: "John"
},
B: {
name: "Ivan"
}
}
// Outputs an array with [KEY, VALUE]
console.log('entries:', _.entries(obj))
console.log('toPairs:', _.toPairs(obj))
// Outputs array with objects containing the keys and values
console.log('entries:', _.map(_.entries(obj), ([k, v]) => ({
[k]: v
})))
console.log('keys:', _.map(_.keys(obj), k => ({
[k]: obj[k]
})))
console.log('transform:', _.transform(obj, (r, c, k) => r.push({
[k]: c
}), []))
console.log('reduce:', _.reduce(obj, (r, c, k) => (r.push({
[k]: c
}), r), []))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Note that in the above examples ES6 is used (arrow functions and dynamic properties).
You can use lodash _.fromPairs and other methods to compose an object if ES6 is an issue.

If you want the key (id in this case) to be a preserved as a property of each array item you can do
const arr = _(obj) //wrap object so that you can chain lodash methods
.mapValues((value, id)=>_.merge({}, value, {id})) //attach id to object
.values() //get the values of the result
.value() //unwrap array of objects

Transforming object to array with plain JavaScript's(ECMAScript-2016) Object.values:
var obj = {
22: {name:"John", id:22, friends:[5,31,55], works:{books:[], films:[]}},
12: {name:"Ivan", id:12, friends:[2,44,12], works:{books:[], films:[]}}
}
var values = Object.values(obj)
console.log(values);
If you also want to keep the keys use Object.entries and Array#map like this:
var obj = {
22: {name:"John", id:22, friends:[5,31,55], works:{books:[], films:[]}},
12: {name:"Ivan", id:12, friends:[2,44,12], works:{books:[], films:[]}}
}
var values = Object.entries(obj).map(([k, v]) => ({[k]: v}))
console.log(values);

Object to Array
Of all the answers I think this one is the best:
let arr = Object.entries(obj).map(([key, val]) => ({ key, ...val }))
that transforms:
{
a: { p: 1, q: 2},
b: { p: 3, q: 4}
}
to:
[
{ key: 'a', p: 1, q: 2 },
{ key: 'b', p: 3, q: 4 }
]
Array to Object
To transform back:
let obj = arr.reduce((obj, { key, ...val }) => { obj[key] = { ...val }; return obj; }, {})
To transform back keeping the key in the value:
let obj = arr.reduce((obj, { key, ...val }) => { obj[key] = { key, ...val }; return obj; }, {})
Will give:
{
a: { key: 'a', p: 1, q: 2 },
b: { key: 'b', p: 3, q: 4 }
}
For the last example you can also use lodash _.keyBy(arr, 'key') or _.keyBy(arr, i => i.key).

2017 update: Object.values, lodash values and toArray do it. And to preserve keys map and spread operator play nice:
// import { toArray, map } from 'lodash'
const map = _.map
const input = {
key: {
value: 'value'
}
}
const output = map(input, (value, key) => ({
key,
...value
}))
console.log(output)
// >> [{key: 'key', value: 'value'}])
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

var arr = _.map(obj)
You can use _.map function (of both lodash and underscore) with object as well, it will internally handle that case, iterate over each value and key with your iteratee, and finally return an array. Infact, you can use it without any iteratee (just _.map(obj)) if you just want a array of values. The good part is that, if you need any transformation in between, you can do it in one go.
Example:
var obj = {
key1: {id: 1, name: 'A'},
key2: {id: 2, name: 'B'},
key3: {id: 3, name: 'C'}
};
var array1 = _.map(obj, v=>v);
console.log('Array 1: ', array1);
/*Actually you don't need the callback v=>v if you
are not transforming anything in between, v=>v is default*/
//SO simply you can use
var array2 = _.map(obj);
console.log('Array 2: ', array2);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
However, if you want to transform your object you can do so, even if you need to preserve the key, you can do that ( _.map(obj, (v, k) => {...}) with additional argument in map and then use it how you want.
However there are other Vanilla JS solution to this (as every lodash solution there should pure JS version of it) like:
Object.keys and then map them to values
Object.values (in ES-2017)
Object.entries and then map each key/value pairs (in ES-2017)
for...in loop and use each keys for feting values
And a lot more. But since this question is for lodash (and assuming someone already using it) then you don't need to think a lot about version, support of methods and error handling if those are not found.
There are other lodash solutions like _.values (more readable for specific perpose), or getting pairs and then map and so on. but in the case your code need flexibility that you can update it in future as you need to preserve keys or transforming values a bit, then the best solution is to use a single _.map as addresed in this answer. That will bt not that difficult as per readability also.

If you want some custom mapping (like original Array.prototype.map) of Object into an Array, you can just use _.forEach:
let myObject = {
key1: "value1",
key2: "value2",
// ...
};
let myNewArray = [];
_.forEach(myObject, (value, key) => {
myNewArray.push({
someNewKey: key,
someNewValue: value.toUpperCase() // just an example of new value based on original value
});
});
// myNewArray => [{ someNewKey: key1, someNewValue: 'VALUE1' }, ... ];
See lodash doc of _.forEach https://lodash.com/docs/#forEach

Related

How to create a builder function that transforms key-value data, and is supplied with both the data and the structure/format of the output

This is a follow-up question to: How to build/transform an object from a previous object
Given data in a key-value format, I want to transform it into a new structure.
// given data example #1
const dataAnimals = {"monkeys": 10, "alligators": 20}
// given data example #2
const dataColors = {"red": 100, "green": 4, "yellow": 17}
And the new structure of the output can be chosen from any prespecified format.
If we stick with dataAnimals as the input, there are three possible output formats:
structure type 1
[
{
label: {
en: "monkeys"
},
value: 10
},
{
label: {
en: "alligators"
},
value: 20
}
]
structure type 2
[
{
count: 10,
label: {
en: "monkeys"
}
},
{
count: 20,
label: {
en: "alligators"
}
}
]
structure type 3
[
{
count: 10,
value: "monkeys"
},
{
count: 20,
value: "alligators"
}
]
I'm likely to have several more types of structures.
I wonder if there's a way to supply the chosen/desired structure (i.e., pass the structure to a builder function alongside the input data itself).
So far, using ramda I could only write 3 separate builder functions with the structure being hard-coded inside each:
const buildArr1 = R.pipe(R.toPairs, R.map(R.applySpec({value: R.nth(1), label: {en: R.nth(0)}})))
const buildArr2 = R.pipe(R.toPairs, R.map(R.applySpec({count: R.nth(1), label: {en: R.nth(0)}})))
const buildArr3 = R.pipe(R.toPairs, R.map(R.applySpec({value: R.nth(0), count: R.nth(1)})))
buildArr1(dataAnimals)
buildArr2(dataAnimals)
buildArr3(dataAnimals)
The differences between buildArr1(), buildArr2(), and buildArr3() are subtle and only in terms of the structure inside applySpec(). So it got me thinking, whether it's possible to have just one function buildArr(), that takes as input both the data to transform and the format to be returned.
// pseudo-code for desired code
buildArr(dataAnimals, structureType1)
// which will return
// [
// {
// label: {
// en: "monkeys"
// },
// value: 10
// },
// {
// label: {
// en: "alligators"
// },
// value: 20
// }
// ]
buildArr(dataAnimals, structureType2)
// [
// {
// count: 10,
// label: {
// en: "monkeys"
// }
// },
// {
// count: 20,
// label: {
// en: "alligators"
// }
// }
// ]
Is there any proper way to do something like that? I'm also not sure what should be the nature of structureType1/structureType2 being passed. An object? Or rather a function that "knows to interact" with the input data?
Please note that while this question revolves around ramda, it seems (to me) to be broader than a specific library.
EDIT
User #Scott Sauyet has nicely pointed out that writing the individual builders (i.e., buildArr1(), buildArr2, etc.) could be achieved with plain JS. This speaks to my point above, that this isn't necessarily a ramda question.
const buildArr1PlainJS = (o) => ({data: Object .entries (o) .map (([k, v]) => ({value: v, label: {en: k}}))})
const buildArr2PlainJS = (o) => ({data: Object .entries (o) .map (([k, v]) => ({count: v, label: {en: k}}))})
const buildArr3PlainJS = (o) => ({data: Object .entries (o) .map (([k, v]) => ({value: k, count: v}))})
I think you can do this fairly easily by passing in a formatting function to a generic processor. It might look something like this:
// main function
const transform = (fn) => (o) => Object .entries (o) .map (([k, v]) => fn (k, v))
// formatting functions
const format1 = transform ((k, v) => ({label: {en: k}, value: v}))
const format2 = transform ((k, v) => ({count: v, label: {en: k}}))
const format3 = transform ((k, v) => ({count: v, value: k}))
// input data
const inputs = {
dataAnimals: {"monkeys": 10, "alligators": 20},
dataColors: {"red": 100, "green": 4, "yellow": 17},
};
// demo
Object .entries (inputs) .forEach (([name, input]) => [format1, format2, format3] .forEach ((format, i) =>
console .log (`${name}, format ${i + 1}:`, format (input))
))
.as-console-wrapper {max-height: 100% !important; top: 0}
Again, I don't see Ramda adding much value here, although we could of course do this with Ramda's tools. We might write:
const transform = (fn) => (o) => map (unapply (fn)) (toPairs (o))
Or if you had a point-free fetish, even this:
const transform = compose (flip (o) (toPairs), map, unapply)
But I don't think these -- especially the latter -- add anything useful.

Transform an array of objects by removing object properties not contained in another array

A long title, so I´ll explain the problem by example. I have an array of objects:
const myObjects = [
{
id: 1,
name: "a",
stuff: "x"
},
{
id: 2,
name: "b",
stuff: "y"
},
];
Then I have another array of objects like this:
const myTemplate=[
{
desiredProperty: "name",
someOtherProperty: "..."
},
{
desiredProperty: "stuff",
someOtherProperty: "..."
},
];
Now I want to transform myObjects array to new one, so that the individual objects contain only the properties listed in desiredProperty of each object in myTemplate.
The result should look like this:
myResult = [
{
name: "a",
stuff: "x"
},
{
name: "b",
stuff: "y"
}
]
How to achieve this?
This approach lets you partially apply the template to get back a reusable function to run against multiple sets of inputs:
const convert = (template, keys = new Set (template .map (t => t .desiredProperty))) => (xs) =>
xs .map (
(x) => Object .fromEntries (Object .entries (x) .filter (([k, v]) => keys .has (k)))
)
const myObjects = [{id: 1, name: "a", stuff: "x"}, {id: 2, name: "b", stuff: "y"}]
const myTemplate= [{desiredProperty: "name", someOtherProperty: "..."}, {desiredProperty: "stuff", someOtherProperty: "..."}]
console .log (
convert (myTemplate) (myObjects)
)
But I agree with the comment that the template here is better expressed as an array of keys to keep.
The following code creates a Set of the keys you want to keep. Then, we map over your myObjects array and only keep the object keys that are in the toKeep Set.
const myObjects=[{id:1,name:"a",stuff:"x"},{id:2,name:"b",stuff:"y"}];
const myTemplate=[{desiredProperty:"name",someOtherProperty:"..."},{desiredProperty:"stuff",someOtherProperty:"..."}];
const toKeep = new Set(myTemplate.map(t => t.desiredProperty));
const newObjs = myObjects.map(o => {
const obj = {};
for (let key in o) {
if (toKeep.has(key)) {
obj[key] = o[key];
}
}
return obj;
});
console.log(newObjs);

Javascript: How to convert and map an object containing objects into an array?

In Javascript, how to map and convert this object:
{
0: {k1 : v1},
2: {k2 : v2}
}
to this array:
[
{
label: k1,
value: v1
},
{
label: k2,
value: v2
}
]
obs.: one-liners are nice, but all answers are welcome.
I couldn't get the desired result, but I have blindly followed formulas like:
const objectMap = (obj, fn) =>
Object.fromEntries(
Object.entries(obj).map(
([k, v], i) => [i, fn(v, k, i)]
)
)
const cfieldsFinal = objectMap(modcf, (k, v) => ({
label: v,
value: k
}))
and that's ALMOST what I need, except, it's still an object:
output => {0: {label: k1, value: v1}, 1: {label: k2, value: v2}}
So, only a complete noob such as myself would get stuck on this part...
You're very close, you just need to construct the object manually. Start by using Array.values() to convert data to an array. Iterate the array with Array.flatMap() to flatten the sub-arrays create by the internal map. Convert each object to an array of [key, value] pairs. Map each pair, and create an object.
const data = {0: { k1: 'v1' }, 2: { k2: 'v2' }}
const result = Object.values(data) // convert data to an array
.flatMap(o => // map to a flattend array
Object.entries(o) // get the entries of each object
.map(([label, value]) => ({ label, value })) // create a new object from each entry
)
console.log(result)
const foo = {
0: {
k1: "v1"
},
2: {
k2: "v2"
}
};
/*
[
{
label: k1,
value: v1
},
{
label: k2,
value: v2
}
]
*/
const oe = Object.entries;
const bar = oe(foo).map(([k, v]) => {
const [label, value] = oe(v)[0];
return {
label,
value
};
});
console.log(bar);

How to create an array of property/value from an object using Ramda?

I have an object like
const obj = {
apple:'red',
banana:'yellow'
}
I need to return an array with properties/values using ramda.
Example:
[
{
name: 'apple',
value:'red'
},
{
name: 'banana',
value:'yellow'
},
]
A ramda solution:
R.pipe(
R.toPairs,
R.map(R.zipObj(['name', 'value']))
)(obj)
You can achieve that without any 3rd party lib, with Object.entries, that returns an array with an array that contains key & value, map over it to convert it to an object.
const obj = {
apple: 'red',
banana: 'yellow'
};
const result = Object.entries(obj)
.map(([name, value]) => ({
name,
value
}));
console.log(result);

(_.merge in ES6/ES7)Object.assign without overriding undefined values

There is _.merge functionality in lodash. I want to achieve the same thing in ES6 or ES7.
Having this snippet:
Object.assign({}, {key: 2}, {key: undefined})
I want to receive {key: 2}. Currently I receive {key: undefined}
This is NOT a deep merge.
Is it possible? If yes then how to achieve that?
You can't achieve that with a straight usage of Object.assign, because each next object will rewrite the same keys for prev merge. The only way, to filter your incoming objects with some hand-crafted function.
function filterObject(obj) {
const ret = {};
Object.keys(obj)
.filter((key) => obj[key] !== undefined)
.forEach((key) => ret[key] = obj[key]);
return ret;
}
You can simply filter out the keys with undefined values before passing them to Object.assign():
const assign = (target, ...sources) =>
Object.assign(target, ...sources.map(x =>
Object.entries(x)
.filter(([key, value]) => value !== undefined)
.reduce((obj, [key, value]) => (obj[key] = value, obj), {})
))
console.log(assign({}, {key: 2}, {key: undefined}))
Write a little utility to remove undefined values:
function removeUndefined(obj) {
for (let k in obj) if (obj[k] === undefined) delete obj[k];
return obj;
}
Then
Object.assign({}, {key: 2}, removeUndefined({key: undefined}))
This seems preferable to writing your own assign with wired-in behavior to remove undefined values.
use lodash to omit nil values and then combine the two objects into one via spread
{ ...(omitBy({key: 2}, isNil)), ...(omitBy({key: undefined}, isNil))}
See more info on lodash here https://lodash.com/docs/4.17.15
With ES2019/ES10's new object method, Object.fromEntries(), Michał's answer can be updated:
const assign = (target, ...sources) =>
Object.assign(target, ...sources.map(x =>
Object.fromEntries(
Object.entries(x)
.filter(([key, value]) => value !== undefined)
)
))
console.log(assign({}, {key: 2}, {key: undefined}))
If you just need the values and don't need an object, you could also use object destructuring:
const input = { a: 0, b: "", c: false, d: null, e: undefined };
const { a = 1, b = 2, c = 3, d = 4, e = 5, f = 6 } = input;
console.log(a, b, c, d, e, f);
// => 0, "", false, null, 5, 6
This will only override absent or undefined values.
I often use this for function argument default values like this:
function f(options = {}) {
const { foo = 42, bar } = options;
console.log(foo, bar);
}
f();
// => 42, undefined
f({})
// => 42, undefined
f({ foo: 123 })
// => 123, undefined
f({ bar: 567 })
// => 42, 567
f({ foo: 123, bar: 567 })
// => 123, 567

Categories