I have an array of objects that has one of the properties("attributes") as array of objects.
var products = [
{
"productId": "1",
"attributes": [
{
"variant":"red",
"price": "134.00"
}
]
},
{
"productId": "2",
"attributes": [
{
"variant": "green",
"value": "3400.00"
},
{
"variant": "pink",
"price": "342.00"
}
]
}
]
I want the nested array of objects to be flattened and copied onto parent but the parent object needs to be duplicated for each of the nested object. (Not sure if I have explained it correctly).
The converted array of objects should be:
var transformedProducts = [
{
"productId": "1",
"variant":"red",
"price": "134.00"
},
{
"productId": "2",
"variant": "green",
"value": "3400.00"
},
{
"productId": "2",
"variant": "pink",
"price": "342.00"
}
]
I can map over the outer array and then again map over the inner array, and in the inner most map, construct a new object.
Is there a better or more functional approach to this?
You can use Array.flatMap() to iterate the objects, and then iterate the attributes with Array.map(), and combine with the rest of the object. The Array.flatMap() would also flatten the array of arrays to a single array.
const fn = arr => arr.flatMap(({ attributes, ...rest }) =>
attributes.map(o => ({
...rest,
...o
}))
)
const products = [{"productId":"1","attributes":[{"variant":"red","price":"134.00"}]},{"productId":"2","attributes":[{"variant":"green","value":"3400.00"},{"variant":"pink","price":"342.00"}]}]
const result = fn(products)
console.log(result)
With Ramda, you can iterate and flatten the array using R.chain. To get an array of attributes combined with their parents, you can use the R.ap as combinator of two functions:
const { chain, ap, pipe, prop, applyTo, omit, mergeRight, map } = R
const fn = chain(ap(
pipe(prop('attributes'), applyTo),
pipe(omit(['attributes']), mergeRight, map)
))
const products = [{"productId":"1","attributes":[{"variant":"red","price":"134.00"}]},{"productId":"2","attributes":[{"variant":"green","value":"3400.00"},{"variant":"pink","price":"342.00"}]}]
const result = fn(products)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
You can use Array#map and Array#flat as follows:
const products = [ { "productId": "1", "attributes": [ { "variant":"red", "price": "134.00" } ] }, { "productId": "2", "attributes": [ { "variant": "green", "value": "3400.00" }, { "variant": "pink", "price": "342.00" } ] } ];
const output = products.map(({productId,attributes}) =>
attributes.map(({variant,price}) => ({productId,variant,price}))
)
.flat();
console.log( output );
Or to only name attributes, in case there are other properties:
const products = [ { "productId": "1", "attributes": [ { "variant":"red", "price": "134.00" } ] }, { "productId": "2", "attributes": [ { "variant": "green", "value": "3400.00" }, { "variant": "pink", "price": "342.00" } ] } ];
const output = products.map(({attributes,...rest}) =>
attributes.map(attribute => ({...rest,...attribute}))
)
.flat();
console.log( output );
This code should work for you:
const transformedProducts = [];
products.forEach((product) => {
product.attributes.forEach((attribute) => {
transformedProducts.push({
productId: product.productId,
...attribute,
});
});
});