Merging nested array using map in JS - javascript

I am fetching a data from Laravel API this way
$inventory = Gifts::with('allocation')->get();
$response = [
'data' => $inventory->toArray(),
]
The output for the above looks like the image below in the console
This is what is inside the 0: {…}
{
"id": 1,
"name": "Bar 1",
"allocation": [
{
"id": 1,
"location_id": "1",
"qty": "2",
},
{
"id": 2,
"location_id": "4",
"qty": "32",
},
{
"id": 3,
"location_id": "7",
"qty": "12",
}
]
}
I'm trying to get an output like this
{
"isEditable": false,
"id": 1,
"name": "Bar 1",
"location1": "2"
"location4": "32"
"location7": "12"
}
It's an array that consists of 100+ entries like this and the allocation can be more or less or maybe empty as well
What I have done so far
const array = result.data(gift => ({ isEditable: false, ...gift }));
This adds "isEditable" field to the array.

You could use Array.prototype.map() to map the result array into a new one that only includes id, name, and the locationNN properties.
In the Array.prototype.map()'s callback:
Use the spread operator to separate the allocation property from the other properties of each array item (call it otherProps e.g.):
Spread the otherProps into a new object, inserting the isEditable property.
Map the allocation items into a key-value pair array, where the key is location_id appended to "location"; and the value is the qty property.
Use Object.fromEntries() on the key-value pair array to create an object, and spread that object into the outer object to be returned.
const output = result.map(r => {
const { allocation, ...otherProps } = r 1️⃣
return {
...otherProps, 2️⃣
isEditable: false,
...Object.fromEntries( 4️⃣
allocation.map(a => [`location${a.location_id}`, a.qty]) 3️⃣
)
}
})
demo

This solution uses reduce
const { allocation, ...rest } = gift
const processed = allocation.reduce((acc, loc, idx) => {
acc[`location${loc.location_id}`] = loc.qty
return acc
}, {})
const result = { ...rest, ...processed }
console.log(result)

Related

Nested array issue in JavaScript

I have the following array
Array["MyArray",
{
"isLoaded":true,
"items":
[{
"id":"4",
"name":"ProductA",
"manufacturer":"BrandA",
"quantity":1,
"price":"25"
},{
"id":"1",
"name":"ProductB",
"manufacturer":"BrandB",
"quantity":5,
"price":"20"
}],
"coupons":null
}
]
I need to load product names and their quantity from the array.
const result = [key, value].map((item) => `${item.name} x ${item.quantity}`);
Here's one possible way to achieve the desired result:
const getProductsAndQuantity = ([k , v] = arr) => (
v.items.map(it => `${it.name} x ${it.quantity}`)
);
How to use it within the context of the question?
localforage.iterate(function(value, key, iterationNumber) {
console.log([key, value]);
const val2 = JSON.parse(value);
if (val2 && val2.items && val2.items.length > 0) {
console.log(val2.items.map(it => `${it.name} x ${it.quantity}`).join(', '))
};
});
How it works?
Among the parameters listed in the question ie, value, key, iterationNumber, only value is required.
The above method accepts the key-value pair as an array (of 2 elements) closely matching the console.log([key, value]); in the question
It uses only v (which is an object). On v, it accesses the prop named items and this items is an Array.
Next, .map is used to iterate through the Array and return each product's name and quantity in the desired/expected format.
Test it out on code-snippet:
const arr = [
"MyArray",
{
"isLoaded": true,
"items": [{
"id": "4",
"name": "ProductA",
"manufacturer": "BrandA",
"quantity": 1,
"price": "25"
}, {
"id": "1",
"name": "ProductB",
"manufacturer": "BrandB",
"quantity": 5,
"price": "20"
}],
"coupons": null
}
];
const getProductsAndQuantity = ([k, v] = arr) => (
v.items.map(
it => `${it.name} x ${it.quantity}`
)
);
console.log(getProductsAndQuantity());
I understood. You should learn about array methods such as map, filter, reduce. Here you go...
const items = [{
"id":"4",
"name":"ProductA",
"manufacturer":"BrandA",
"quantity":1,
"price":"25"
},{
"id":"1",
"name":"ProductB",
"manufacturer":"BrandB",
"quantity":5,
"price":"20"
}];
const result = items.map((item) => `${item.name} x ${item.quantity}`);
console.log(result);
I think I understand the question to say that the input is an array of objects, each containing an array of items. The key is that a nested array requires a nested loop. So, we iterate the objects and their internal items (see the lines commented //outer loop and // inner loop below)
Also, half-guessing from the context, it looks like the that the OP aims to assemble a sort of invoice for each object. First a demo of that, (and see below for the version simplified to exactly what the OP asks)...
const addInvoice = obj => {
let total = 0;
// inner loop
obj.invoice = obj.items.map(i => {
let subtotal = i.quantity * i.price;
total += subtotal
return `name: ${i.name}, qty: ${i.quantity}, unit price: ${i.price}, subtotal: ${subtotal}`
});
obj.invoice.push(`invoice total: ${total}`);
}
const objects = [{
"isLoaded": true,
"items": [{
"id": "4",
"name": "ProductA",
"manufacturer": "BrandA",
"quantity": 1,
"price": "25"
}, {
"id": "1",
"name": "ProductB",
"manufacturer": "BrandB",
"quantity": 5,
"price": "20"
}],
"coupons": null
}]
// outer loop
objects.forEach(addInvoice);
console.log(objects);
If my guess about the goal went to far, just remove the unit price, subtotal and total lines from the invoice function...
const objects = [{
"isLoaded": true,
"items": [{
"id": "4",
"name": "ProductA",
"manufacturer": "BrandA",
"quantity": 1,
"price": "25"
}, {
"id": "1",
"name": "ProductB",
"manufacturer": "BrandB",
"quantity": 5,
"price": "20"
}],
"coupons": null
}]
const summaryString = obj => {
return obj.items.map(i => `${i.name}, ${i.quantity}`);
}
const strings = objects.map(summaryString);
console.log(strings);

Remove array of object if object inside is empty

I am preparing an array like this
datas[5] = { "qty_sized": "1", "resolution": "5", "status": "", "order": 1342 };
where [5] is dynamic from response.
I have and object mydata and inside that I have a object items.
I push array to object items, with assign
Object.assign(mydatadata.items, datas);
Now mydata.items has an array set,
`
items{
1 {qty_auth: "", resolution: "4", status: "", order: "1495"},
5 {qty_sized: "1", resolution: "4", status: "", order: "1485"}
}`
Now if qty_auth: "" , from which i need to check if qty_ is empty then remove the array . So expected output is something like this:
Note: qty_ is dynamic here.
items{ 5 {qty_sized: "1", resolution: "4", status: "", order: "1485"} }
and i want to result inside same object mydata.items
I tried something like this
const mydatadata.items = mydata.items.filter((o) =>
Object.keys(o).some((k) => k.startsWith("qty") && o[k])
);
console.log(result);
but its now giving me any output
Using Object#entries, get the key-value pairs of items
Using Array#filter, iterate over the above array
In each iteration, check if the current item has a key starting with qty_ whose value is not empty. You can do this using Object#keys, Array#some, and String#startsWith.
Using Object#fromEntries, convert the filtered pairs to an object again.
const obj = {
items: {
1: {qty_auth: "", resolution: "4", status: "", order: "1495"},
5: {qty_sized: "1", resolution: "4", status: "", order: "1485"}
}
};
obj.items = Object.fromEntries(
Object.entries(obj.items)
.filter(([_, item]) =>
Object.keys(item).some(key => key.startsWith('qty_') && item[key])
)
);
console.log(obj);
You're talking about an array, but using curly brackets instead of square brackets. For filter() to work it would have to look like:
mydata = {
items: [
{qty_auth: "", resolution: "4", status: "", order: "1495"},
{qty_sized: "1", resolution: "4", status: "", order: "1485"}
]
}
Assuming it is an actual array there's still a problem with "const mydatadata.items", or at least it throws an error for me because mydatadata is not initialized. Unless it's a typo and it should be mydata, but then you'd be redeclaring it. So depending on what you want:
mydata.items = mydata.items.filter((o) =>
Object.keys(o).some((k) => k.startsWith("qty") && o[k])
);
or
let mydatadata = {};
mydatadata.items = mydata.items.filter((o) =>
Object.keys(o).some((k) => k.startsWith("qty") && o[k])
);
Furthermore you're storing the result in mydatadata but you're logging the variable result.
So depending on the previous answer:
console.log(mydatadata);
or
console.log(mydata);
Here's a fiddle: https://jsfiddle.net/b57qa82d/
You should probably just be using an array rather than an object. It's not really clear from your question what structure you need as you keep changing the terminology to describe your data. For example: "Now mydata.items has an array set" but your code says that it should be object with keys, not an array.
So I suggest: get your data in an array, and filter it by iterating over each object's entries and checking to see if any of the keys starting with "qty" has a value that isn't an empty string. You can then assign that filtered array to myObject.items.
const data = [
{ "qty_sized": "0", "resolution": "5", "status": "", "order": 2 },
{ "qty_auth": "", "resolution": "5", "status": "", "order": 3 },
{ "qty_auth": "1", "resolution": "5", "status": "", "order": 1342 },
{ "qty_sized": "", "resolution": "2", "status": "", "order": 1 },
{ "qty_sized": "1", "resolution": "1", "status": "", "order": 142 }];
const filtered = data.filter(obj => {
return Object.entries(obj).find(([key, value]) => {
return key.startsWith('qty') && value;
});
});
const myObject = { items: filtered };
console.log(myObject);
Additional documentation
Object.entries
find

Removing unwanted object keys & undefined in Javascript?

This is my array:
const
array1 = [
{
"value": "0",
"name": "5",
"waste": "remove",
"city": "NY"
},
{
"value": "0",
"name": "51",
"waste": "remove",
}
]
So now, i wanted to remove certain and form a new array with objects: For example, i need to remove "Waste & value" and keep rest of the things, so i used this code:
var keys_to_keep = ['name', 'city']
const result = array2.map(e => {
const obj = {};
keys_to_keep.forEach(k => obj[k] = e[k])
return obj;
});
console.log(result)
And it gives a output as
[ { name: '5', city: 'NY' }, { name: '51', city: undefined } ]
Now as you can see city with undefined value, how to remove that ? i mean filter this and just show keys with value,
So my question is how to filter undefined and also is there any other better solution for removing unwanted object keys and showing new array with wanted keys ? or the method am using is performant enough ?
You can check if the value is undefined in your forEach:
const result = array2.map(e => {
const obj = {};
keys_to_keep.forEach(k => {
if (undefined !== e[k]) {
obj[k] = e[k]
}
)
return obj;
});
You can check if e[k] is defined before you add it to obj by checking whether the e object has the property k using .hasOwnProperty():
const array = [{ "value": "0", "name": "5", "waste": "remove", "city": "NY" }, { "value": "0", "name": "51", "waste": "remove", } ];
const keys_to_keep = ['name', 'city'];
const result = array.map(e => {
const obj = {};
keys_to_keep.forEach(k => {
if (e.hasOwnProperty(k))
obj[k] = e[k]
});
return obj;
});
console.log(result)
If the keys you want to remove aren't dynamic, you can also use destructuring assignment to pull out the properties you want to discard, and use the rest syntax to obtain an object without those properties:
const array = [{ "value": "0", "name": "5", "waste": "remove", "city": "NY" }, { "value": "0", "name": "51", "waste": "remove", } ];
const result = array.map(({value, waste, ...r}) => r);
console.log(result)
I am going to answer both the parts. So here are the steps to do that.
Use map() on the main array.
Get entries of each object using Object.entries().
Apply filter() on entires array are remove those entires for which key is not present in keys_to_keep
Now for the second part.
Using keys_to_keep create an object which contain undefined values for each key.
Use map() again on prev result and use Spread operator. First spread the object created above and then spread the original values. This way if any key is not found it will be set to undefined
const
array1 = [
{
"value": "0",
"name": "5",
"waste": "remove",
"city": "NY"
},
{
"value": "0",
"name": "51",
"waste": "remove",
}
]
var keys_to_keep = ['name', 'city']
let obj = Object.fromEntries(keys_to_keep.map(x => [x, undefined]));
const res = array1.map(obj =>
Object.fromEntries(
Object.entries(obj).filter(([k, v]) => keys_to_keep.includes(k))))
.map(x => ({...obj, ...x}))
console.log(res)
You can use .map to iterate over the objects, Object.entries to get the key-value pairs of each item, Object.fromEntries to group them into the resulting objects, and .filter to get only the entries with a key in keys_to_keep and a value that is not undefined:
const array1 = [
{ "value": "0", "name": "5", "waste": "remove", "city": "NY" },
{ "value": "0", "name": "51", "waste": "remove" }
];
var keys_to_keep = ['name', 'city'];
const result = array1.map(item =>
Object.fromEntries(
Object.entries(item).filter(([key, value]) =>
keys_to_keep.includes(key) && value !== undefined
)
)
);
console.log(result)

ES6 map array of objects to array

There is an array of objects
const data = [{
"name": "08/20/2018",
"id": "name_1"
}, {
"name": "12/23/2018",
"id": "name_2"
}]
and I would like to map this array of objects in order to get just array
["Date 1","08/20/2018","Date 2","12/23/2018"]
I'm trying using .map()
data.map((d, i) =>
`${'Date ' + i}`
d.name
)];
but cannot map name with the first (d) parameter.
Because the input items and output array items aren't one-to-one, you won't be able to use .map. Use reduce instead:
const data = [{
"name": "08/20/2018",
"id": "name_1"
}, {
"name": "12/23/2018",
"id": "name_2"
}];
const output = data.reduce((a, { name }, i) => {
a.push('Date ' + (i + 1), name);
return a;
}, []);
console.log(output);
Or .flatMap:
const data = [{
"name": "08/20/2018",
"id": "name_1"
}, {
"name": "12/23/2018",
"id": "name_2"
}];
const output = data.flatMap(({ name }, i) => (['Date ' + (i + 1), name]));
console.log(output);
(note that since arrays are zero-indexed, you'll have to use i + 1, not i, if you want the first item in the output array to start at 1 instead of 0)
You can't use map since that method produce a new array with the same number of items of the original ones.
However, you can use flatMap (where supported) to achieve the your desired result:
data.flatMap(({name}, i) => [`Date ${i + 1}`, name]);
console.log(data) // [ "Date 1", "08/20/2018", "Date 2", "12/23/2018" ]
Basically flatMap is like calling map and then flat; therefore if from the callback function we returns an array per item, this array will be flattened before returned.
Regular map call would have been produced [[ "Date 1", "08/20/2018"], ["Date 2", "12/23/2018"]] instead.
Try to combine map and flatmap methods in order to achieve desired result:
const data = [{
"name": "08/20/2018",
"id": "name_1"
}, {
"name": "12/23/2018",
"id": "name_2"
}];
const result = data.map((s, i)=> [`Date ${i}`, s.name]).flatMap(f=> f);
console.log(result)
or using flat method:
const data = [{
"name": "08/20/2018",
"id": "name_1"
}, {
"name": "12/23/2018",
"id": "name_2"
}];
const result = data.map((s, i)=> [`Date ${i}`, s.name]).flat(1);
console.log(result)
One line answer using ES2019 Array.flat :
data.map((item,index)=>([`Date${index+1}`,item.name])).flat();
But in my opinion, it is not optimized when there is huge data.
I appreciate above answers but if you still prefer to use .map() method to accomplish your work, you can do it.
Just with an additional use of concat() method with map() method. Let's see how.
I have used ...data,map() statement where ... is used for Array destructuring. More information can be found at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Array_destructuring.
const data = [
{
"name": "08/20/2018",
"id": "name_1"
},
{
"name": "12/23/2018",
"id": "name_2"
}
]
output = new Array() // or just []
output = output.concat(...data.map((obj, index) => [`Date ${index + 1}`, obj.name]))
console.log(output)
// [ 'Date 1', '08/20/2018', 'Date 2', '12/23/2018' ]
Screenshot

Unable to convert a nested object into an array of objects and viceversa

I am having an object of the following kind:
var sourceObj = {
"feature1": [
{"id":"1","name":"abc","enabled":false,"type":"type1"},
{"id":"2","name":"xyz","enabled":false,"type":"type1"}
]
,
"feature2": [
{"id":"3","name":"lmn","enabled":true,"type":"type2"},
{"id":"4","name":"pqr","enabled":false,"type":"type2"}
]
}
Need to get converted to an array of objects of the following type:
var destArr = [
{ "feature_name":"feature1",
"feature_details":[
{"id":"1","name":"abc","enabled":false,"type":"type1"},
{"id":"2","name":"xyz","enabled":true,"type":"type1"}
]
},
{ "feature_name":"feature2",
"feature_details":[
{"id":"3","name":"lmn","enabled":true,"type":"type2"}
{"id":"4","name":"pqr","enabled":false,"type":"type2"}
]
}
]
I have tried the following approaches for conversion of source object to resultant array of objects and resultant array of objects back to the source object
//Converting source object to array of objects
let arr = Object.keys(sourceObj).map(key => {
return sourceObj[key];
})
converting array of objects back to source objetc
let obj = Object.assign({}, ...destArr.map(item => ({ [item.name]: item.value })));
You could use Object.entries to map everything in a single shot.
To go back to the original structure, you could use reduce against the generated array (see backToOriginalArray below)
var sourceObj = {
"feature1": [{
"id": "1",
"name": "abc",
"enabled": false,
"type": "type1"
},
{
"id": "2",
"name": "xyz",
"enabled": false,
"type": "type1"
}
],
"feature2": [{
"id": "3",
"name": "lmn",
"enabled": true,
"type": "type2"
},
{
"id": "4",
"name": "pqr",
"enabled": false,
"type": "type2"
}
]
};
// Step 1: use object.entries against the original object to build an array of objects.
var destArray = Object.entries(sourceObj).map(([key, value]) => ({
"feature_name": key,
"feature_details": value
}));
console.log(destArray);
// Step 2: use reduce against the generated array to get an object with the same structure of the original one.
var backToOriginalArray = destArray.reduce((acc, {feature_name, feature_details}) => (acc[feature_name] = feature_details, acc), {});
console.log(backToOriginalArray);
Map the Object.entries of your initial object:
var sourceObj = {
"feature1": [
{"id":"1","name":"abc","enabled":false,"type":"type1"},
{"id":"2","name":"xyz","enabled":false,"type":"type1"}
],
"feature2": [
{"id":"3","name":"lmn","enabled":true,"type":"type2"},
{"id":"4","name":"pqr","enabled":false,"type":"type2"}
]
};
const destArr = Object.entries(sourceObj).map(
([feature_name, feature_details]) => ({ feature_name, feature_details })
);
console.log(destArr);
To go the other way around, use Object.fromEntries:
const arr=[{feature_name:"feature1",feature_details:[{id:"1",name:"abc",enabled:!1,type:"type1"},{id:"2",name:"xyz",enabled:!1,type:"type1"}]},{feature_name:"feature2",feature_details:[{id:"3",name:"lmn",enabled:!0,type:"type2"},{id:"4",name:"pqr",enabled:!1,type:"type2"}]}];
const obj = Object.fromEntries(arr.map(
({ feature_name, feature_details }) => [feature_name, feature_details]
));
console.log(obj);

Categories

Resources