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?
CodePudding user response:
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:
- Extracts the attributes array, and then applies it to the 2nd function.
- Gets the rest of the object (without the attributes), and creates a mapping function that merges it to the iterated objects.
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>
CodePudding user response:
This code should work for you:
const transformedProducts = [];
products.forEach((product) => {
product.attributes.forEach((attribute) => {
transformedProducts.push({
productId: product.productId,
...attribute,
});
});
});
CodePudding user response:
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 );