Converting an array of objects into an object of objects - javascript

Recently, I've been learning on how to retrieve objects from an array and I've had my fair share of converting an array of objects into a singular object of objects, but I couldn't wrap my head around this one!
dataOfCars.js:
const dataOfCars = [
{
id: "car1",
owner: { license: "license001", name: "driver1" },
mechanic: [
{
id: "mechanic1",
owner: { license: "license001", name: "driver2" }
}
]
},
{
id: "car2",
owner: { license: "license021", name: "driver2" },
mechanic: []
},
{
id: "car3",
owner: { license: "license002", name: "driver2" },
mechanic: [
{
id: "mechanic1",
owner: { license: "license002", name: "driver2" }
}
]
}
];
module.exports = dataOfCars;
I know there are several examples in regards to this but the output I'm expecting is quite different as it involves nested objects:
{
cars : {
idOrder : {
"car1" : {
id : "car1",
owner : "owner1",
mechanic : ["mechanic1", "mechanic2"]
},
...
},
result : ["car1", "car2", ...]
},
mechanics : {
idOrder : {
"mechanic1" : {
id : "mechanic1",
owner : "driver2",
},
...
},
result : ["mechanic1", ...]
},
owners : {
idOrder : {
"driver1" : {
license : "license001",
name: "driver1",
},
...
},
result : ["driver1", "driver2", ...]
}
}
I gave it a go at Sandbox: https://codesandbox.io/s/old-morning-hx9m1. I used Object.assign({}, ...dataOfCars) but it only 'spreads' the whole array, thus returning the final element only per entity. Here's the code at index.js:
import dataOfCars from "./data.js";
import { normalize, schema } from "normalizr";
const ownerSchema = new schema.Entity("owners");
const mechanicSchema = new schema.Entity("mechanics", {
mechanic: ownerSchema
});
const carSchema = new schema.Entity("cars", {
owner: ownerSchema,
mechanics: [mechanicSchema]
});
/* This method somehow returns the very final element */
const objectPost = Object.assign({}, ...dataOfCars);
const normalizedPost = normalize(objectPost, carSchema);
// The normalized object returning the 2/3 expected entities
console.log(normalizedPost);
I'm still relatively new to ES6 and I would love to know how this works out in the end! Thanks for the help in advance!

Related

How to limit an array field in javascript object to a certain length

I know this may a simple problem but I have a the following javascript object:
const categories = {
title: 'cat1',
contents: [
{
name: 'cont1'
},
{
name: 'cont2'
},
{
name: 'cont3'
}
]
}
How can a transform this categories object so it has only 2 contents element as an example?
const transformedCategories = {
title: 'cat1',
contents: [
{
name: 'cont1'
},
{
name: 'cont2'
}
]
}
A couple of ways to do it:
const transformedCategories = {
title: categories.title,
contents: categories.contents.slice(0,2) // you can also use splice here
}
Another, slightly cheeky way:
const contents = [...categories.contents];
contents.length = 2;
const transformedCategories = {...categories, contents}

How do you check if value exist in object of object's array?

I want to know which logic i should use to check every object's array of parent object contained in grand parent object
Hi guys i want to check if this value for example : "127.0.0.1" exists in this object (MyObject has like 2k objects in it)
{
"name" : MyObject
"value": [
{
"name" : "Object1",
"properties":{
"address" : [
"13.65.25.19/32",
"13.66.60.119/32",
]
}
},
{
"name" : "Object2",
"properties":{
"address" : [
"13.65.25.19/32",
"127.0.0.1",
]
}
}
]
}
Btw does include() needs to match the whole string or for example if 127.0.0.1 is like this in my object 127.0.0.1/32, i can still retrieve it even if there is a ip range ?
Your data is structured quite specifically, so you can write a custom method which you can call over and over again. It will check for a
const obj = {
name: 'MyObject',
value: [
{
name: 'Object1',
properties: {
address: ['13.65.25.19/32', '13.66.60.119/32'],
},
},
{
name: 'Object2',
properties: {
address: ['13.65.25.19/32', '127.0.0.1'],
},
},
],
};
const address = '127.0.0.1';
const includesAddress = (address) => {
for (const val of obj.value) {
if (val.properties.address.some((a) => address === a)) return true;
}
return false;
};
console.log(includesAddress(address));
Array.flatMap implementation
const obj = {
name: 'MyObject',
value: [
{
name: 'Object1',
properties: {
address: ['13.65.25.19/32', '13.66.60.119/32'],
},
},
{
name: 'Object2',
properties: {
address: ['13.65.25.19/32', '127.0.0.1'],
},
},
],
};
const address = '127.0.0.1';
const output = obj.value.flatMap(item => item.properties.address).includes(address);
console.log(output);
If you want check if the partial ip addess is included in the list, you should make use of a regex implementation.
Sample Implementation
const obj = {
name: 'MyObject',
value: [
{
name: 'Object1',
properties: {
address: ['13.65.25.19/32', '13.66.60.119/32'],
},
},
{
name: 'Object2',
properties: {
address: ['13.65.25.19/32', '127.0.0.1'],
},
},
],
};
const address = '13.65.25.19';
const regex = new RegExp(address, 'i')
const output = obj.value.flatMap(item => item.properties.address).filter(x => regex.test(x)).length > 0;
console.log(output);

Map the nested data from other table using promise and async-await

I need the expert advice for this code. I need to know Is there any better way to solve this.
I am using the mongoose for db. I have a dataset like this:
Below is matchTable:
{
_id: 617bc0113176d717f4ddd6ce,
car: [],
status: true
},
{
_id: 617bc0113176d717f4ddd6cg,
car: [
{
aid: '5c1b4ffd18e2d84b7d6febcg',
}
],
status: true
}
And I have a Car table in which car name is there on behalf of id
like this
{ _id: ObjectId('5c1b4ffd18e2d84b7d6febce'), name: 'ford' },
{ _id: ObjectId('5c1b4ffd18e2d84b7d6febcg'), name: 'mitsubishi' },
So I want to make join the data from car table, so that response get name on behalf of aid.
Desired result will be like
{
_id: 617bc0113176d717f4ddd6ce,
car: [],
status: true
},
{
_id: 617bc0113176d717f4ddd6cg,
car: [
{
aid: '5c1b4ffd18e2d84b7d6febcg',
name: 'mitsubishi'
}
],
status: true
}
For that I have to merge the car table on matchTable. I have done this but I want to give some suggestion that is there any better way to do or is it fine. I need expert advice.
const getData = await matchTable.find(
{ status: true }
).lean().exec();
let dataHolder = [];
await Promise.all (
getData.map(async x => {
await Promise.all(
x.car.map(async y => {
let data = await Car.findOne(
{ _id: ObjectId(y.aid) },
{ name: 1 }
).lean().exec();
y.name = '';
if (data) {
y.name = data.name;
}
})
)
// If I return { ...x }, then on response it will return {}, {} on car column
dataHolder.push(x) //So I have chosen this approach
})
);
Please guide me if any better and efficient solution is there. Thanks in advance
You can make use of aggregation here.
const pipeline = [
{
$match : { status : true }
},
{
$unwind: '$matchtable',
},
{
$lookup: {
from: "cars",
localField: "car.aid",
foreignField: "_id",
as: "matchcars"
}
},
{
$addFields: {
"car.carName": { $arrayElemAt: ["$matchcars.name", 0] }
}
},
{
$group: {
_id: "$_id",
cars: { $push: "$matchcars" }
}
}
]
const result = await matchTable.aggregate(pipeline).exec();
Please make sure, aid field inside car array (in matchTable collection) is an ObjectId because its being matched to _id (which is an ObjectId) inside cars collection.

Updated nested object by matching ID

I have an array with nested objects that I need to update from another array of objects, if they match.
Here is the data structure I want to update:
const invoices = {
BatchItemRequest: [
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "11110" },
},
Amount: 2499,
},
],
},
},
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10111" },
},
Amount: 2499,
},
],
},
},
],
};
Here is the array of objects I want to update it from:
const accounts = [
{ AccountCode: "10110", Id: "84" },
{ AccountCode: "11110", Id: "5" },
{ AccountCode: "10111", Id: "81" },
];
I want to update invoices, using accounts, by inserting Id if AccountCode matches, to get the following structure:
const invoices = {
BatchItemRequest: [
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110", Id: "84" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "11110", Id: "5" },
},
Amount: 2499,
},
],
},
},
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110", Id: "84" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10111", Id: "81" },
},
Amount: 2499,
},
],
},
},
],
};
I have tried various methods, such as the following:
const mapped = invoices.BatchItemRequest.map((item1) => {
return Object.assign(
item1,
accounts.find((item2) => {
return item2 && item1.Invoice.Line.ItemAccountRef.AccountCode === item2.AccountCode;
})
);
});
Problem with this approach (it doesn't work as I think I need to do another nested map), but it also creates a new array, only including the nested elements of invoices.
Does anyone know a good approach to this?
This isn't the cleanest of code but it gets the job done:
function matchInvoiceWithAccount(invoices, accounts) {
const mappedInvoices = invoices.BatchItemRequest.map((request) => {
// Shouldn't modify input parameter, could use Object.assign to create a copy and modify the copy instead for purity
request.Invoice.Line = request.Invoice.Line.map((line) => {
const accountCode = line.SalesItemLineDetail.ItemAccountRef.AccountCode;
// If accounts was a map of AccountCode to Id you would't need to search for it which would be more effective
const account = accounts.find((account) => account.AccountCode === accountCode);
if (account) {
line.SalesItemLineDetail.ItemAccountRef.Id = account.Id;
}
return line;
});
return request;
});
return {
BatchItemRequest: mappedInvoices,
};
}
What you could and probably should do to improve this is to not modify the input parameters of the function, but that requires that you in a better way copy the original, either using Object.assign or spread operator.
At first, it will be good to create Map from your accounts array. We will go one time for array with O(n) and then will read ids by code with O(1). And nested fors is O(m*n), that will be much more slower at big arrays.
const idsByAccountCodes = new Map();
accounts.forEach((data) => {
idsByAccountCodes.set(data.AccountCode, data.Id);
})
or shorter:
const idsByAccountCode = new Map(accounts.map((data) => [data.AccountCode, data.Id]))
then if you want to mutate original values you can go through all nesting levels and add values
for ( const {Invoice:{ Line: line }} of invoices.BatchItemRequest){
for ( const {SalesItemLineDetail: {ItemAccountRef: item}} of line){
item.Id = idsByAccountCodes.get(item.AccountCode) || 'some default value'
// also if you don't have ids for all codes you need to define logic for that case
}
}
If you don't need to mutate original big object "invoices" and all of nested objects, then you can create recursive clone of if with something like lodash.cloneDeep

Merge the object using typescript

In my angular application i am having the data as follows,
forEachArrayOne = [
{ id: 1, name: "userOne" },
{ id: 2, name: "userTwo" },
{ id: 3, name: "userThree" }
]
forEachArrayTwo = [
{ id: 1, name: "userFour" },
{ id: 2, name: "userFive" },
{ id: 3, name: "userSix" }
]
newObj: any = {};
ngOnInit() {
this.forEachArrayOne.forEach(element => {
this.newObj = { titleOne: "objectOne", dataOne: this.forEachArrayOne };
})
this.forEachArrayTwo.forEach(element => {
this.newObj = { titleTwo: "objectTwo", dataTwo: this.forEachArrayTwo };
})
console.log({ ...this.newObj, ...this.newObj });
}
In my real application, the above is the structure so kindly help me to achieve the expected result in the same way..
The working demo https://stackblitz.com/edit/angular-gyched which has the above structure.
Here console.log(this.newObj) gives the last object,
titleTwo: "ObjectTwo",
dataTwo:
[
{ id: 1, name: "userFour" },
{ id: 2, name: "userFive" },
{ id: 3, name: "userSix" }
]
but i want to combine both and need the result exactly like the below..
{
titleOne: "objectOne",
dataOne:
[
{ id: 1, name: "userOne" },
{ id: 2, name: "userTwo" },
{ id: 3, name: "userThree" }
],
titleTwo: "ObjectTwo",
dataTwo:
[
{ id: 1, name: "userFour" },
{ id: 2, name: "userFive" },
{ id: 3, name: "userSix" }
]
}
Kindly help me to achieve the above result.. If i am wrong in anywhere kindly correct with the working example please..
You're assigning both values to this.newObj, so it just overwrites the first object.
Also, there is no need for your loop. It doesn't add anything.
Instead, you can do:
this.newObjA = { titleOne: "objectOne", dataOne: this.forEachArrayOne };
this.newObjB = { titleTwo: "objectTwo", dataTwo: this.forEachArrayTwo };
console.log({ ...this.newObjA, ...this.newObjB });
**
EDIT **
Having spoken to you regarding your requirements, I can see a different solution.
Before calling componentData, you need to make sure you have the full data. To do this, we can use forkJoin to join the benchmark requests, and the project requests into one Observable. We can then subscribe to that Observable to get the results for both.
The code would look something like this:
createComponent() {
let benchmarks, projects;
let form = this.productBenchMarkingForm[0];
if (form.benchmarking && form.project) {
benchmarks = form.benchmarking.filter(x => x.optionsUrl)
.map(element => this.getOptions(element));
projects = form.project.filter(x => x.optionsUrl)
.map(element => this.getOptions(element));
forkJoin(
forkJoin(benchmarks), // Join all the benchmark requests into 1 Observable
forkJoin(projects) // Join all the project requests into 1 Observable
).subscribe(res => {
this.componentData({ component: NgiProductComponent, inputs: { config: AppConfig, injectData: { action: "add", titleProject: "project", dataProject: this.productBenchMarkingForm[0] } } });
})
}
}
getOptions(element) {
return this.appService.getRest(element.optionsUrl).pipe(
map((res: any) => {
this.dataForOptions = res.data;
element.options = res.data;
return element;
})
)
}
Here is an example in Stackblitz that logs the data to the console

Categories

Resources