how to calculate the missing quantity and items in javascript? - javascript

I have a product which is made of many x materials, each material can be made of other materials or be pure. If they are made of other materials, it is specified which materials are need with their needed quantity and currently available quantity for the item. I want to calculate how many material I have and I don't have, and for the material I don't have, I want to sum all the missing quantities.
Right know I am getting right the material quantity in the "first level" when the product is missing a material which is not used in submaterials. When I try to sum the missing submaterials the operation is not right.
How can I calculate the materials needed to create a product and each materials?
Here is the code I write:
const product = {
name: 'prod_1',
quantity: 1.2,
material_quantities: [
{ id: 2, quantity: 2 },
{ id: 1, quantity: 2 },
],
};
const materials = [
{
id: 1,
name: 'material1',
quantity: 1.2,
material_quantities: null,
},
{
id: 2,
name: 'material2',
quantity: 1.2,
material_quantities: [{ id: 1, quantity: 2 }],
},
];
function areEnoughMaterials(materialId, neededQuantity) {
const mainMaterial = materials.find(id === materialId);
const inStock = parseFloat(neededQuantity) <= parseFloat(mainMaterial.quantity);
if (mainMaterial?.material_list?.length === 0 && inStock) {
materialsStatus.available[materialId] = mainMaterial.quantity;
}
if (!inStock) {
if (!materialsStatus.missing[materialId]) {
materialsStatus.missing[materialId] =
parseFloat(neededQuantity) - parseFloat(mainMaterial.quantity);
} else {
materialsStatus.missing[materialId] =
materialsStatus.missing[materialId] + parseFloat(mainMaterial.quantity);
}
}
if (mainMaterial.material_quantities) {
for (const material of mainMaterial.material_quantities) {
areEnoughMaterials(material.id, material.quantity);
}
}
return;
}
let materialsStatus = {
available: {},
missing: {},
};
for (const material of product.material_quantities) {
areEnoughMaterials(material.id, material.quantity);
}
The output I am expecting is this:
{
available: { 2:1.2, 1:1.2 },
missing:{ 2:0.8, 1:3.6 }
}

Related

Combine two one dimensional arrays to create a cartesian table

I hope you are good.
I am struggling to create a compatible data type in javascript to display a cartesian like table where we have a vertical and a horizontal header.
Basically I have 3 one dimensional arrays where the first two are the table headers, and the third has the combination of those two by id's (basically the table cells).
let horizontal_header = [
{ id: 1, name: 'h1' },
{ id: 2, name: 'h2' },
];
let vertical_header = [
{ id: 10, name: 'r1' },
{ id: 11, name: 'r2' },
];
let cells = [
{ hid: 1, vid: 10, id: 7, name: 'c1' },
{ hid: 1, vid: 11, id: 8, name: 'c2' },
{ hid: 2, vid: 10, id: 9, name: 'c3' },
{ hid: 2, vid: 11, id: 10, name: 'c4' },
],
Also it can happen that a combination might not exists in that case, I want to enter an empty cell or something obvious that this cell is missing.
I want to create a table like below:
h1
h2
r1
c1
c3
r2
c2
c4
I would appreciate any suggestion and be very thankful to help me solve this complex use-case using Angular for rendering the table template.
Thank you.
I'd approach this problem by parsing the cells into more table-render friendly format like below. Note: I used ### separator, you can use anything that suits for coding practice.
let output = {};
cells.forEach(cell => {
output[cell.hid + '###' + cell.vid] = {
id: cell.id,
name: cell.name,
};
});
After that, you can use the output object to render the table cell as you already know the combination of hid and vid. You can prepare/render your table rows as below.
const rows = [];
for (let i = 0; i < horizontal_header.length; i++) {
const row = [];
for (let j = 0; j < vertical_header.length; j++) {
const hid = horizontal_header[i];
const vid = vertical_header[j];
if (output[hid + '###' + vid]) {
row.push(output[hid + '###' + vid].name);
} else {
row.push('-');
}
}
rows.push(row);
}

How do I filter values in an object to display an array of values

How do I fix my honorRoll function at the bottom so that it displays the names of all the students with GPA higher than the benchmark of 3.5?
Right now all I get back is an empty array, I would like to see [Bianca Pargas, Sameer Fares].
const GPA_BENCHMARK = 3.5;
let students = {
1: {
name: 'Egill Vignission',
gpa: 3.4
},
2: {
name: 'Bianca Pargas',
gpa: 3.8
},
3: {
name: 'Aisling O\'Sullivan',
gpa: 3.4
},
4: {
name: 'Sameer Fares',
gpa: 3.9
}
}
let honorRoll = Object.values(students).filter(student => {
return students.gpa >= GPA_BENCHMARK;
});
console.log(honorRoll);
in a very simple way you can do like that
const GPA_BENCHMARK = 3.5;
let students = {
1: {
name: 'Egill Vignission',
gpa: 3.4,
},
2: {
name: 'Bianca Pargas',
gpa: 3.8,
},
3: {
name: "Aisling O'Sullivan",
gpa: 3.4,
},
4: {
name: 'Sameer Fares',
gpa: 3.9,
},
};
let honorRoll = [];
for (let key in students) {
if (students[key].gpa >= GPA_BENCHMARK) {
honorRoll.push(students[key]);
}
}
console.log(honorRoll);
You used students instead of student in your filter fn

Apply Combo Discount to a Food Order

An app lets users order food from a menu. The menu has three types of selection: main, drink and dessert. A feature needs to be added which will discount the price by 10% for every main+drink combo (10% off every combo). All items ordered by the customer are stored in an array like so:
order = [
{id: 4, count: 1, type: "main", price: 10}
{id: 5, count: 2, type: "drink", price: 9.5}
]
As you can see, each item the customer orders has a count property. How can I apply the discount without mutating the order array or any of the object properties? Ideally I'd like to loop through the array, determine total number of combos (in the example above it would be 1), determine the total discount value and pass that value to another function which computes the order total. If anyone can suggest a better way of doing it, I'm all ears (or eyes in this case).
Also, what is the best way to express this problem from a technical point of view?
const userOrder = [
{ id: 4, count: 1, type: "main", price: 200 },
{ id: 5, count: 1, type: "drink", price: 100 }
];
const orderInfo = userOrder.reduce((acc, cur) => {
console.log('cur', cur)
if (acc[cur.type]) {
return {
...acc,
[cur.type]: cur.count,
totalAmount: (cur.count * acc.totalAmount)
}
} else {
return {
...acc,
[cur.type]: cur.count,
totalAmount: (cur.count * cur.price ) + acc.totalAmount
}
}
}, {
main: 0,
drink: 0,
totalAmount: 0
});
const noOfComobosPresent = Math.min(orderInfo.main, orderInfo.drink);
const totalDiscountValue = noOfComobosPresent * 10;
const finalAmount = orderInfo.totalAmount - ((orderInfo.totalAmount * totalDiscountValue ) / 100) ;
console.log('finalAmount', finalAmount)

Generate a new array with count of property values

I have an array in my state :
projects: [
{ title: 'todo 1', person: 'Sam', status: 'ongoing'},
{ title: 'project', person: 'Jack', status: 'complete' },
{ title: 'Design video', person: 'Tim', status: 'complete' },
{ title: 'Create a forum', person: 'Jade', status: 'overdue' },
{ title: 'application', person: 'Jade', status: 'ongoing'},],
From this array (projects), I would like to generate a new array with Javascript and to get this result :
totalByPersonAndStatus : [
{person : 'Sam', complete: 0, ongoing: 1, overdue: 0 },
{person : 'Jack', complete: 1, ongoing: 0, overdue: 0 },
{person : 'Tim', complete: 1, ongoing: 0, overdue: 0 },
{person : 'Jade', complete: 0, ongoing: 1, overdue: 1 },]
I tried it
totalProjectsByPersonAndStatus: state => {
state.projects.forEach(name => {
state. totalByPersonAndStatus["name"] = name.person;
});
return state. totalByPersonAndStatus;
The problem, if a make a console.log(this.totalByPersonAndStatus) I have an object with only the data of projects.name [name: "Jade", __ob__: Observer]
Can you help me ?
Thank you
You can use reduce
let projects =[{title:'todo1',person:'Sam',status:'ongoing'},{title:'project',person:'Jack',status:'complete'},{title:'Designvideo',person:'Tim',status:'complete'},{title:'Createaforum',person:'Jade',status:'overdue'},{title:'application',person:'Jade',status:'ongoing'},]
let desired = projects.reduce((output,{person,status}) => {
if( output[person] ){
output[person][status]++
} else {
output[person] = {
person,
complete: Number(status==='complete'),
ongoing: Number(status==='ongoing'),
overdue: Number(status==='overdue')
}
}
return output;
},{})
console.log(Object.values(desired))
Create a new Set for people and statuses by iterating through the projects, a set has only unique values so sets are a convenience, iterate through your people set creating a new object with all the statuses initialized to 0, then iterate over the projects to increment the various statuses that apply. This method allows any number of new statuses to be added without changing the code - dynamic.
var people = new Set();
var status = new Set();
projects.forEach((p)=>{
people.add(p.person);
status.add(p.status);
});
var totalByPersonAndStatus = [];
people.forEach((person)=>{
let peeps = { "person": person };
status.forEach((stat)=>{
peeps[stat] = 0;
});
projects.forEach((project)=>{
if (project.person === person) { peeps[project.status]++; }
});
totalByPersonAndStatus.push(peeps);
});
You could use reduce and destructuring like this:
const projects=[{title:'todo 1',person:'Sam',status:'ongoing'},{title:'project',person:'Jack',status:'complete'},{title:'Design video',person:'Tim',status:'complete'},{title:'Create a forum',person:'Jade',status:'overdue'},{title:'application',person:'Jade',status:'ongoing'}]
const merged = projects.reduce((acc,{person,status})=>{
acc[person] = acc[person] || { person, ongoing:0, complete:0, overdue:0}
acc[person][status]++;
return acc;
},{})
console.log(Object.values(merged))
The goal is create an object merged with each person as key and then increment based on the statuses:
{
"Sam": {
"person": "Sam",
"ongoing": 1,
"complete": 0,
"overdue": 0
},
"Jack": {
}
...
}
Then use Object.values, to get the final array.
You could make it a one-liner:
const projects=[{title:'todo 1',person:'Sam',status:'ongoing'},{title:'project',person:'Jack',status:'complete'},{title:'Design video',person:'Tim',status:'complete'},{title:'Create a forum',person:'Jade',status:'overdue'},{title:'application',person:'Jade',status:'ongoing'}],
output = Object.values(projects.reduce((a,{person,status})=>
((a[person] = a[person] || {person,ongoing:0,complete:0,overdue:0})[status]++,a),{}))
console.log(output)

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