Merge array of objects inside loop into new object - javascript

Trying to create a new object, and then join all the objects from nested values from a JSON file.
The JSON data is rather large, so have taken a sample, and called it var items
Problem I am having is that the nested data is not updating the new object.
var items = [
{
"id": 11,
"title": "Fruit Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Fruit order for 1 person",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Melon",
"otherName": "Watermelon"
},
{
"itemName": "Apple",
"otherName": "Red apple"
}
]
},
{
"id": 12,
"title": "Canned Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Canned order for 2 people",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Tomatoes",
"otherName": "Diced tomato"
}
]
},
{
"id": 13,
"title": "Dairy Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Dairy Order for 2 people",
"storeNames": [
"Store 1"
],
"items": []
}
]
;
var copyItems = [];
for (let i = 0; i < items.length; i++) {
items[i].allItems = items[i].items;
copyItems.push(items[i])
}
console.log(copyItems);
var copyItems = copyItems.map(function(elem){
return elem.allItems;
}).join(",");
console.log(`These are the final items ${copyItems}`);
I am able to create the new object, and add the nested arrays to this. However I am trying to get the allItems object to display the information like the following:
[
{
"id": 11,
"allItems": "Melon, Apple",
"title": "Fruit Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Fruit order for 1 person",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Melon",
"otherName": "Watermelon"
},
{
"itemName": "Apple",
"otherName": "Red apple"
}
]
},
{
"id": 12,
"allItems": "Tomatoes",
"title": "Canned Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Canned order for 2 people",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Tomatoes",
"otherName": "Diced tomato"
}
]
},
{
"id": 13,
"allItems": "",
"title": "Dairy Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Dairy Order for 2 people",
"storeNames": [
"Store 1"
],
"items": []
}
]
Here is my JSFiddle: https://jsfiddle.net/buogdvx9/6/
Javascript is still a language I am learning and working through, and some things still catch me out.
Thank you.

You can use Array.map() to create the new array, then using some destructuring to create each new element in this array.
We create the allitems property on each new element by first mapping the sub items array to get a list of subitem names, then using Array.join() to create a comma separated string.
The arrow function you see as the first argument to Array.map is another way of writing function(args) { .. }.
const items = [ { "id": 11, "title": "Fruit Test", "releaseDateTime": "2021-10-21T10:50:00+09:30", "mainContent": "Fruit order for 1 person", "storeNames": [ "Store 1" ], "items": [ { "itemName": "Melon", "otherName": "Watermelon" }, { "itemName": "Apple", "otherName": "Red apple" } ] }, { "id": 12, "title": "Canned Test", "releaseDateTime": "2021-10-21T10:50:00+09:30", "mainContent": "Canned order for 2 people", "storeNames": [ "Store 1" ], "items": [ { "itemName": "Tomatoes", "otherName": "Diced tomato" } ] }, { "id": 13, "title": "Dairy Test", "releaseDateTime": "2021-10-21T10:50:00+09:30", "mainContent": "Dairy Order for 2 people", "storeNames": [ "Store 1" ], "items": [] } ];
const result = items.map(({ id, ...rest}) => {
return {
id,
allItems: rest.items.map(el => el.itemName).join(', '),
...rest
};
});
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Since you only want to update the existing object, using forEach to loop over each item in array, then loop over the prosperity with a map operator to get the array with itemName.
items.forEach((obj) => {
obj.allItems = obj.items.map((item) => item.itemName)
});
console.log(items)
Simple example:
// iterating over the items
for (let i = 0; i < items.length; i++) {
let currentItem = items[i];
currentItem.allItems = []; // adding new empty array
for (let j = 0; j < currentItem.items.length; j++) {
currentItem.allItems.push(currentItem.items[j].itemName);
}
}
Working Example:
var items = [
{
"id": 11,
"title": "Fruit Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Fruit order for 1 person",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Melon",
"otherName": "Watermelon"
},
{
"itemName": "Apple",
"otherName": "Red apple"
}
]
},
{
"id": 12,
"title": "Canned Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Canned order for 2 people",
"storeNames": [
"Store 1"
],
"items": [
{
"itemName": "Tomatoes",
"otherName": "Diced tomato"
}
]
},
{
"id": 13,
"title": "Dairy Test",
"releaseDateTime": "2021-10-21T10:50:00+09:30",
"mainContent": "Dairy Order for 2 people",
"storeNames": [
"Store 1"
],
"items": []
}
]
;
// since you only want to update the existing object, using map to loop over each item in array
items.forEach((obj) => {
// using map to create the new array of just itemNames
obj.allItems = obj.items.map((item) => item.itemName)
});
console.log(items)

Just use the combination of Array.map and Array.join
Logic
Since you want to create a new array, run Array.map on the parent array.
On each nodes in the parent return the whole node with one extra key allItems.
For allItems create a new array from items array in each node and join then with space
var items = [{"id":11,"title":"Fruit Test","releaseDateTime":"2021-10-21T10:50:00+09:30","mainContent":"Fruit order for 1 person","storeNames":["Store 1"],"items":[{"itemName":"Melon","otherName":"Watermelon"},{"itemName":"Apple","otherName":"Red apple"}]},{"id":12,"title":"Canned Test","releaseDateTime":"2021-10-21T10:50:00+09:30","mainContent":"Canned order for 2 people","storeNames":["Store 1"],"items":[{"itemName":"Tomatoes","otherName":"Diced tomato"}]},{"id":13,"title":"Dairy Test","releaseDateTime":"2021-10-21T10:50:00+09:30","mainContent":"Dairy Order for 2 people","storeNames":["Store 1"],"items":[]}];
const newItems = items.map(node => ({ ...node, allItems: node.items.map(item => item.itemName).join(" ")}));
console.log(newItems);

Related

How to transform two Objects into one overview in Javascript?

I am currently struggling with the formatting of a map operation on two object arrays in Javascript.
So lets say we have two arrays:
var customer = [
{ "Name": "Thomas", "Address": "example street 34", "customerID": 1 },
{ "Name": "Alica", "Address": "example street 24", "customerID": 2 },
{ "Name": "John", "Address": "example bouelvard 4", "customerID": 3 }
]
var orders = [
{ "Product": "iPhone 12", "Amount": 2, "customerID": 1 },
{ "Product": "charger", "Amount": 1, "customerID": 1 },
{ "Product": "screen protection", "Amount": 5, "customerID": 2 }
]
I want to have a result array so that when I print it out, I have an overview over customers with their orders in this way:
{
customer: {
"Name": "Thomas",
"Address": "example street 34",
"customerID": 1,
},
order: [
{
"Product": "iPhone 12",
"Amount": 2,
"customerID": 1
},
{
"Product": "charger",
"Amount": 1,
"customerID": 1
}
]
}
So I basically did a map function and searched for orders with the same customer id.
let overview = customers.map(element1 => ({ ...element1, : [...(orders.filter(element => element.customerID === element1.customerID))] }));
This is what I get:
{
"Name": "Thomas",
"Address": "example street 34",
"customerID": 1,
"order": [[Object], [Object]]
}
How do I get the "customer:" before the output of the customer objects and why do I get the Object output in my order array?
try with that:
customer.map(element1 => (
{...element1, order: orders.filter(element => element.customerID === element1.customerID)}
)))
You were missing 'order' key and also don't need to spread since filter returns an array (empty if nothing filtered)
You're almost there:
let overview = customer.map(customer => ({
customer,
order: orders.filter(order => customer.customerID === order.customerID)
}))
You are so close! So very close. You can rename customer to customers just to avoid confusion the element1 becomes customer and the rest is as shown below:
const
customers = [ { "Name": "Thomas", "Address": "example street 34", "customerID": 1 }, { "Name": "Alica", "Address": "example street 24", "customerID": 2 }, { "Name": "John", "Address": "example bouelvard 4", "customerID": 3 } ],
orders = [ { "Product": "iPhone 12", "Amount": 2, "customerID": 1 }, { "Product": "charger", "Amount": 1, "customerID": 1 }, { "Product": "screen protection", "Amount": 5, "customerID": 2 } ],
custOrders = customers
.map(
customer =>
({
customer,
orders:orders
.filter(order => order.customerID === customer.customerID)
})
);
console.log( custOrders );
First you can create map from orders array where customerId will be key and array with all orders belonging to one customer will be map value.
Next is initialising empty results array and iterating customers array. While iterating, push new object to results array. New object should contain 2 fields, customer and order. customer is object from iteration and order is map value which you get from previously generate map using customer.customerID as map key.
This way, performance are increased because fetching data from Map data structure has O(1) time complexity. Using filter method to find all orders for specific customer is time consuming with complexity O(n).
const customers = [{"Name": "Thomas", "Address": "example street 34", "customerID": 1},{"Name": "Alica", "Address": "example street 24", "customerID": 2}, {"Name": "John", "Address": "example boulevard 4", "customerID": 3}];
const orders = [{"Product": "iPhone 12", "Amount": 2, "customerID": 1},{"Product": "charger", "Amount": 1, "customerID": 1},{"Product": "screen protection", "Amount": 5, "customerID": 2}];
const ordersMap = new Map();
for (const order of orders) {
const { customerID } = order;
const mapValue = ordersMap.get(customerID);
if (mapValue) {
mapValue.push(order);
} else {
ordersMap.set(customerID, [order]);
}
}
const results = [];
for (const customer of customers) {
results.push({
customer,
order: ordersMap.get(customer.customerID),
});
}
console.log(results)

How can I convert an array of objects to array with a unique id in javascript?

I have a array of object like this :
{
"data": [
{
"id": 1,
"from": "2022-08-01",
"to": "2022-08-05",
"description": "test 1",
"files": [
{
"id": 1,
"hospital_name": "hospital 11",
"hospital_id": 11,
"period_id": 1
},
{
"id": 2,
"hospital_name": "hospital 11",
"hospital_id": 11,
"period_id": 1
}
]
},
{
"id": 2,
"from": "2022-08-06",
"to": "2022-08-10",
"description": "test 2",
"files": [
{
"id": 3,
"hospital_name": "hospital 12",
"hospital_id": 12,
"period_id": 2
},
{
"id": 4,
"hospital_name": "hospital 12",
"hospital_id": 12,
"period_id": 2
}
]
}
]
}
I want to convert the array to be like this :
{
"data": [
{
"id": 1, // this is period id
"hospital_name": "hospital 11",
"hospital_id": 11,
"from": "2022-08-01",
"to": "2022-08-05",
"description": "test 1"
},
{
"id": 2,
"hospital_name": "hospital 12",
"hospital_id": 12,
"from": "2022-08-06",
"to": "2022-08-10",
"description": "test 2"
}
]
}
So I expect the results like that
I try to type my code like this :
data.flatMap((period) =>
period.files.map((file) => ({
id: period.id,
hospital_name: file.hospital_name,
hospital_id: file.hospital_id,
from: period.from,
to: period.to,
description: period.description,
}))
)
But the problem is my code show duplicate id
How can I solve my problem?
Note :
Every period only has one hospital id
You don't need flatMap, just run map on periods and for each period, pick the first file to extract hospital_id and hospital_name like this:
data.map(period => {
const file = period.files[0];
return {
id: period.id,
hospital_name: file.hospital_name,
hospital_id: file.hospital_id,
from: period.from,
to: period.to,
description: period.description,
}
});
You can filter the last result like:
const j = {
"data": [{
"id": 1,
"from": "2022-08-01",
"to": "2022-08-05",
"description": "test 1",
"files": [{
"id": 1,
"hospital_name": "hospital 11",
"hospital_id": 11,
"period_id": 1
},
{
"id": 2,
"hospital_name": "hospital 11",
"hospital_id": 11,
"period_id": 1
}
]
},
{
"id": 2,
"from": "2022-08-06",
"to": "2022-08-10",
"description": "test 2",
"files": [{
"id": 3,
"hospital_name": "hospital 12",
"hospital_id": 12,
"period_id": 2
},
{
"id": 4,
"hospital_name": "hospital 12",
"hospital_id": 12,
"period_id": 2
}
]
}
]
}
console.log(j.data.flatMap((period) =>
period.files.map((file) => ({
id: period.id,
hospital_name: file.hospital_name,
hospital_id: file.hospital_id,
from: period.from,
to: period.to,
description: period.description,
}))
).filter((value, index, self) =>
index === self.findIndex((t) => (
t.id === value.id
))))
Reference:
Array.prototype.filter()
Since the objects in the files array are identical (other than the id) you can destructure the properties from the data array, and (within that) the first object of each files array, and return a new object.
const data={data:[{id:1,from:"2022-08-01",to:"2022-08-05",description:"test 1",files:[{id:1,hospital_name:"hospital 11",hospital_id:11,period_id:1},{id:2,hospital_name:"hospital 11",hospital_id:11,period_id:1}]},{id:2,from:"2022-08-06",to:"2022-08-10",description:"test 2",files:[{id:3,hospital_name:"hospital 12",hospital_id:12,period_id:2},{id:4,hospital_name:"hospital 12",hospital_id:12,period_id:2}]}]};
const out = data.data.map(obj => {
const {
from,
to,
files: [{ period_id, id, ...rest }],
description
} = obj;
return { id: period_id, from, to, description, ...rest }
});
console.log(out);
Additional information
Destructuring assignment
Rest parameters
Spread syntax

a function for data filtration based on two arrays of strings in Javascript

I need some help with array filtration. I want to filter an array of objects based on:
Note: these arrays can be empty. When they are empty, the function should return the original array (without data filtration)
brands: ["brand 1", "brand 2", "brand 3", "brand 4"],
tags: ["tag1", "tag2", "tag 3", "tag 4"],
The array of objects which I want to do filter looks like this:
[
{
"tags": [
"tag1"
],
"price": 10.99,
"name": "Sample name",
"manufacturer": "brand 1",
},
{
"tags": [
"tag1", "tag2"
],
"price": 10.99,
"name": "Sample name",
"manufacturer": "brand 1",
},
{
"tags": [
"tag1", "tag3", "tag4"
],
"price": 10.99,
"name": "Sample name",
"manufacturer": "brand 4",
},
{
"tags": [
"tag1", "tag2"
],
"price": 10.99,
"name": "Sample name",
"manufacturer": "brand1 ",
},
]
the function I have looks like this doing filtration on the manufacturer only:
const obj = {
brands: ["brand 1", "brand 2"],
tags: ["tag1", "tag2", "tag 4"],
}
const filterArray = (obj, array) => {
let newArray = []
const byBrands = array.filter((item) =>
obj.brands.includes(item["manufacturer"].toLowerCase())
)
if (byBrands.length > 0) {
newArray = byBrands
} else {
newArray = array
}
return newArray;
}
I need a function to do filtration on tags and manufacturer at the same time.
Thanks, Stakoverflow :)
You should use keys in your filter object that match properties in the objects to be filtered, otherwise there's no way to know which property compare. Other than that it's just a matter of checking if every() property in the filter object has some() matching entry in the object being filtered. The example ignores empty arrays in the filter object using an OR (||) short-circuit, and uses concat() to evaluate every property as an array.
(You can fine tune to make it case-insensitive, search for substrings, etc.)
const input = [{ "tags": ["tag1"], "price": 10.99, "name": "Sample name", "manufacturer": "brand 1", }, { "tags": ["tag1", "tag2"], "price": 10.99, "name": "Sample name", "manufacturer": "brand 1", }, { "tags": ["tag 4"], "price": 10.99, "name": "Sample name", "manufacturer": "brand 2", }, { "tags": ["tag 3"], "price": 10.99, "name": "Sample name", "manufacturer": "brand 1", },]
const obj = {
manufacturer: ["brand 1", "brand 2"],
tags: ["tag1", "tag2", "tag 4"],
name: [], // ignores empty arrays
}
function filterProducts(array, filters) {
return array.filter(p =>
Object.entries(filters).every(([k, fs]) =>
!fs.length || fs.some(f => [].concat(p[k]).some(t => t === f)))
)
}
console.log(filterProducts(input, obj))

Group Javascript object based on Array Object list

I'm trying to find a way to convert this list of objects based on the group array. The tricky part I've found is iterating through the group Array and applying the object to more than one place if there are multiple groups.
I'm also trying to ignore any group that does not belong to anything. I've tried using the reduce function but I cannot get the iteration through the group array.
let cars =
[
{
"group":[],
"name": "All Makes",
"code": ""
},
{
"group":["Group A"],
"name": "BMW",
"code": "X821"
},
{
"group":["Group B"],
"name": "Audi",
"code": "B216"
},
{
"group":["Group B"],
"name": "Ford",
"code": "P385"
},
{
"group":["Group B", "Group C"],
"name": "Mercedes",
"code": "H801"
},
{
"group":["Group C"],
"name": "Honda",
"code": "C213"
}
]
To become this:
let cars = {
"Group A": [
{
name: "BMW",
code: "X821",
}
],
"Group B": [
{
name: "Audi",
code: "B216"
},
{
name: "Ford",
code: "P385"
},
{
name: "Mercedes",
code: "H801"
}
],
"Group C":[
{
name: "Mercedes",
code: "H801"
},
{
name:"Honda",
code: "C213"
}
]
};
I already tried using reduce to accomplish this but the grouping doesn't replicate if it's in more than one group.
let result = cars.reduce(function(x, {group, name}){
return Object.assign(x, {[group]:(x[group] || [] ).concat({group, name})})
}, {});
Any pointers to help with this would be much appreciated.
You can use .reduce() to loop through each car object in cars. For each group array for a given car, you can then use .forEach() to then add that group as a key to the accumulator. If the group has already been set in the accumulator, you can grab the grouped array of objects, otherwise, you can create a new array []. Once you have an array you can then add the object to the array using .concat(). Since we're using .forEach() on the group array, it won't add the object to the accumulated object if it is empty as .forEach() won't iterate over an empty array.
See example below:
const cars = [{ "group":[], "name": "All Makes", "code": "" }, { "group":["Group A"], "name": "BMW", "code": "X821" }, { "group":["Group B"], "name": "Audi", "code": "B216" }, { "group":["Group B"], "name": "Ford", "code": "P385" }, { "group":["Group B", "Group C"], "name": "Mercedes", "code": "H801" }, { "group":["Group C"], "name": "Honda", "code": "C213" } ];
const res = cars.reduce((acc, {group, ...r}) => {
group.forEach(key => {
acc[key] = (acc[key] || []).concat({...r}); // copy r so it is a different reference for each grouped array
});
return acc;
}, {});
console.log(res);
Some basic approach. #Nick's is much better.
let cars = [{
"group": [],
"name": "All Makes",
"code": ""
},
{
"group": ["Group A"],
"name": "BMW",
"code": "X821"
},
{
"group": ["Group B"],
"name": "Audi",
"code": "B216"
},
{
"group": ["Group B"],
"name": "Ford",
"code": "P385"
},
{
"group": ["Group B", "Group C"],
"name": "Mercedes",
"code": "H801"
},
{
"group": ["Group C"],
"name": "Honda",
"code": "C213"
}
]
let newCars = {};
cars.forEach(o => {
o.group.forEach(g => {
if (!newCars[g])
newCars[g] = [];
newCars[g].push({
name: o.name,
code: o.code
});
});
});
console.log(newCars);

Group objects by multiple properties including nested properties and sump up their values

it's like Group objects by multiple properties in array then sum up their values
but with nested elements and more complicated. I've been struggling for hours.
I have an array of products:
a product looks like this :
{
"commissioningDate": "2019-09-27",
"_product": {
"_id": "aaa",
"name": "Installation"
},
"zones": [
{
"_zone": {
"_id": "KK",
"name": "Zone kk"
},
"category": "category1",
"zone_quantity": 6
}
],
"product_quantity": 3
}
Expected behavior
I made this gist because the example is too long.
Problem
So I have an array of products.
1) products in this array are considered duplicates only if both their commissioningDate and _product._id are the same
2) if many products gets merged into a single product:
we need a to sum-up the product_quantity
we need to merge the zone array inside the product if possible otherwise add it to the array
3) zones of a merged product in this array are considered duplicates only if both their _zone._id and category are the same
4) if many zones gets merged into a single zone we need a to sum-up the zone_quantity
Assume your single product zones always has length 1.
const sample = [
{
"commissioningDate": "2019-09-27",
"_product": {
"_id": "aaa",
"name": "Installation"
},
"zones": [
{
"_zone": {
"_id": "KK",
"name": "Zone kk"
},
"category": "category1",
"zone_quantity": 6
}
],
"product_quantity": 3
},
{
"commissioningDate": "2019-09-27",
"_product": {
"_id": "aaa",
"name": "Installation"
},
"zones": [
{
"_zone": {
"_id": "KK",
"name": "Zone kk"
},
"category": "category2",
"zone_quantity": 3
}
],
"product_quantity": 2
},
{
"commissioningDate": "2019-09-27",
"_product": {
"_id": "aaa",
"name": "Installation"
},
"zones": [
{
"_zone": {
"_id": "KK",
"name": "Zone kk"
},
"category": "category2",
"zone_quantity": 4
}
],
"product_quantity": 5
},
{
"commissioningDate": "2019-09-27",
"_product": {
"_id": "aaa",
"name": "Installation"
},
"zones": [
{
"_zone": {
"_id": "CC",
"name": "Zone cc"
},
"category": "category2",
"zone_quantity": 6
}
],
"product_quantity": 1
},
{
"commissioningDate": "2019-09-27",
"_product": {
"_id": "bbbb",
"name": "Installation"
},
"zones": [
{
"_zone": {
"_id": "CC",
"name": "Zone cc"
},
"category": "category2",
"zone_quantity": 8
}
],
"product_quantity": 2
},
{
"commissioningDate": "2019-09-26",
"_product": {
"_id": "bbbb",
"name": "Installation"
},
"zones": [
{
"_zone": {
"_id": "CC",
"name": "Zone cc"
},
"category": "category2",
"zone_quantity": 8
}
],
"product_quantity": 2
}
]
//reduce initialze value is an empty object
const res = sample.reduce((group, item) => {
//for each item, generate a key k by combining item commissioningDate and item _product._id seperated //by a comma
const k = `${item.commissioningDate},${item._product._id}`;
//check if this key k exists in our object group(which is an empty object when we check the first //item)
//if it is not in the object, we save the key k and its value which is current item into the object //group
if(!group[k]) group[k] = Object.assign({}, item);
//if it is in the object already
else{
//we sum up current item quantity to the group of this item
group[k].product_quantity+=item.product_quantity;
//find index of zone in current group zones has the same zone id and category as item's
for(const itemZone of item.zones){
const zoneIdx = group[k].zones.findIndex(zone => zone._zone._id === itemZone._zone._id && zone.category === itemZone.category)
//index is -1, it's not in group zones, we push the zone to group zones array
if(zoneIdx === -1){
group[k].zones.push(itemZone)
}
//in group zones, we sum up zone_quantity
else{
group[k].zones[zoneIdx].zone_quantity += itemZone.zone_quantity
}
}
}
//update current group
return group
}, {})
//recall keys are our custom identifier for different groups of items, values are actually groups of //items, so we only need to get values from group object
console.log(Object.values(res))

Categories