How can I output multiple results from an array - javascript

I have this array of objects, that initially only had name and unit fields. I recently added a unitConv field of array type.
I used to output an array of strings with the name and unit from every object.
const ingredients = [
{name: 'wine', unit: 'ml', unitConv: []},
{name: 'salt', unit: 'gr', unitConv: [{unitMeasure: { name: 'spoon'}}, {unitMeasure: { name: 'tea-spoon'}}]},
{name: 'onion', unit: 'piece', unitConv: []},
]
const response = ingredients.map(ing => `${ing.name} [${ing.unit}]`)
And this is the response:
["wine [ml]", "salt [gr]", "onion [piece]"]
Now that I added unitConv, I want to see if any unitConv are available in the object, and pass those too, as options, like this:
["wine [ml]", "salt [gr]", "onion [piece]", "salt[spoon]", "salt[tea-spoon]"]
And I want to keep the initial value of salt too, the one the uses the 'gr' as a unit. So for salt, because I have one unit and two unitConv, I want to output it three times, with each of this options.
If one of the objects doesn't have unitConv, the unitConv fields will appear as an empty array, like in the example above.

You can use Array#flatMap to create the second array to concatenate with the first.
const ingredients = [
{name: 'wine', unit: 'ml', unitConv: []},
{name: 'salt', unit: 'gr', unitConv: [{unitMeasure: { name: 'spoon'}}, {unitMeasure: { name: 'tea-spoon'}}]},
{name: 'onion', unit: 'piece', unitConv: []},
]
const response = ingredients.map(ing => `${ing.name} [${ing.unit}]`)
.concat(ingredients.flatMap(({name, unitConv})=>
unitConv.map(x => `${name} [${x.unitMeasure.name}]`)));
console.log(response);

a simple array reduce method:
const ingredients =
[ { name: 'wine', unit: 'ml', unitConv: [] }
, { name: 'salt', unit: 'gr', unitConv:
[ { unitMeasure: { name: 'spoon' } }
, { unitMeasure: { name: 'tea-spoon' } }
]
}
, { name: 'onion', unit: 'piece', unitConv: [] }
]
const resp2 = ingredients.reduce((resp,{name,unit,unitConv}) =>
{
resp.push( `${name} [${unit}]` )
unitConv.forEach(({unitMeasure}) =>
resp.push(`${name} [${unitMeasure.name}]`))
return resp
},[])
console.log( resp2 )
.as-console-wrapper{max-height:100% !important;top: 0;}

you can use flatMap and inside its callback function check for unitConv array lentgh, if it's true then you can use a map for that. here is the demo:
const ingredients = [{
name: 'wine',
unit: 'ml',
unitConv: []
},
{
name: 'salt',
unit: 'gr',
unitConv: [{
unitMeasure: {
name: 'spoon'
}
}, {
unitMeasure: {
name: 'tea-spoon'
}
}]
},
{
name: 'onion',
unit: 'piece',
unitConv: []
},
]
function strFormat(name, unit) {
return `${name} [${unit}]`;
}
const result = ingredients.flatMap((ing) => {
if (ing.unitConv.length) {
const ingAllUnits = ing.unitConv.map((unit) => strFormat(ing.name, unit.unitMeasure.name));
ingAllUnits.push(strFormat(ing.name, ing.unit));
return ingAllUnits;
} else return strFormat(ing.name, ing.unit);
});
console.log(result);

Related

Filtering array based on selected object in JS

Trying to get the filtered array based on the selected object. How can I loop through damaged array which is inside the object and get the resultant array? I tried to add another condition using .map but it prints the rest of the items as well.
Below is the snippet
const inventory = [{
name: 'Jeep',
id: '100',
damaged: [{
name: 'Wrangler',
id: '200'
},
{
name: 'Sahara',
id: '201'
}
]
}, {
name: 'Audi',
id: '101',
damaged: [{
name: 'Q3',
id: '300'
}]
}]
const purchasedCars = [{
car: 'Jeep',
id: '100'
}, {
car: 'Jeep - Wrangler',
id: '200',
},
{
car: 'Jeep - Sahara',
id: '201'
},
{
car: 'Audi - Q3',
id: '300'
}
]
const selectedCar = purchasedCars[0];
const filterCars = () => {
const result = purchasedCars.filter((inv) => inv.id === selectedCar.id)
console.log('result -->', result);
}
filterCars();
Expected output is
[{
car: 'Jeep',
id: '100'
},
{
car: 'Jeep - Wrangler',
id: '200',
},
{
car: 'Jeep - Sahara',
id: '201'
}]
Could anyone please help?
Trying to read your mind here. Is this what you want?
const inventory = [{
name: 'Jeep',
id: '100',
damaged: [{
name: 'Wrangler',
id: '200'
},
{
name: 'Sahara',
id: '201'
}
]
}, {
name: 'Audi',
id: '101',
damaged: [{
name: 'Q3',
id: '300'
}]
}]
const purchasedCars = [{
car: 'Jeep',
id: '100'
}, {
car: 'Jeep - Wrangler',
id: '200',
},
{
car: 'Jeep - Sahara',
id: '201'
},
{
car: 'Audi - Q3',
id: '300'
}
]
const selectedCar = purchasedCars[0];
const filterCars = () => {
let result;
const parentItem = inventory.filter((inv) => inv.id === selectedCar.id)[0];
if ("damaged" in parentItem) {
result = [selectedCar, ...(parentItem.damaged)];
}
console.log('result -->', result);
}
filterCars();
Note that if you can have more nested car types in the damaged property you would you to call filterCars recursively and pass in the car object. If you also want to filters items that may also be present in the damaged property, then you would first need to use the flatMap method (before the filter).

get total price from array of objects per specific user using .reduce in javascript

I have this array of objects
const items = [
{
id: '121',
itemDate: '2022-04-28',
itemName: 'testname1',
itemCategory: 'Category A',
itemPrice: { price: '100', currency: 'GBP' },
createdBy: {
username: 'user1',
name: 'Name 1',
date: '2022-04-28T22:41:59',
},
},
{
id: '122',
itemDate: '2022-04-28',
itemName: 'testname2',
itemCategory: 'Category B',
itemPrice: { price: '100', currency: 'GBP' },
createdBy: {
username: 'user2',
name: 'Name 2',
date: '2022-04-28T22:42:44',
},
},
{
id: '122',
itemDate: '2022-04-28',
itemName: 'testname3',
itemCategory: 'Category C',
itemPrice: { price: '200', currency: 'GBP' },
createdBy: {
username: 'user2',
name: 'Name 2',
date: '2022-04-28T22:43:16',
},
},
]
Code I'm using:
items.reduce(function (c, x) {
if (!c[x.createdBy.username])
c[x.createdBy.username] = {
username: x.createdBy.username,
total: 0,
}
c[x.createdBy.username].total += Number(x.itemPrice.price)
return c
}, [])
This part gives me the following output:
items :>> [
user1: { username: 'user1', total: 100},
user2: { username: 'user2', total: 300}
]
So I tried this to get rid of the object names:
let output = []
let totalSum = 0
for (const username in items) {
let temp = {
username: items[username].username,
total: items[username].total,
}
totalSum = totalSum + items[username].total
output.push(temp)
}
output.push({ username: 'allUsers', total: totalSum })
return output
And final output is as I want it now:
output :>> [
{ username: 'user1', total: 100 },
{ username: 'user2', total: 300 },
{ username: 'allUsers', total: 400}
]
My two questions...
Is there a way to update the .reduce part so that I'd get an object without the name at the beggining, without having to use the for loop?
Is there also a way to implement the part that would sum up all the totals?
Thank you
Code Sample (without comments/description)
const groupAndAdd = arr => (
Object.values(
arr.reduce(
(acc, {createdBy : {username}, itemPrice: {price}}) => {
acc.allUsers ??= { username: 'allUsers', total: 0};
acc.allUsers.total += +price;
if (username in acc) {
acc[username].total += +price;
} else {
acc[username] = {username, total: +price};
}
return acc;
},
{}
)
)
);
Presented below is a working demo to achieve the desired objective, with notes/comments to help understand.
Code Snippet
// method to group by user and sum prices
const groupAndAdd = arr => (
// extract the values from the intermediate result-object
Object.values(
arr.reduce( // generate result as object
(acc, {createdBy : {username}, itemPrice: {price}}) => {
// above line uses de-structuring to directly access username, price
// below uses logical nullish assignment to set-up "allUsers"
acc.allUsers ??= { username: 'allUsers', total: 0};
// accumulate the "price" to the all-users "total"
acc.allUsers.total += +price;
// if "acc" (accumulator) has "username", simply add price to total
if (username in acc) {
acc[username].total += +price;
} else {
// create an object for the "username" with initial total as "price"
acc[username] = {username, total: +price};
}
// always return the "acc" accumulator for ".reduce()"
return acc;
},
{} // initially set the "acc" to empty object
)
) // if required, use ".sort()" to move the all-users to last position in array
);
const items = [{
id: '121',
itemDate: '2022-04-28',
itemName: 'testname1',
itemCategory: 'Category A',
itemPrice: {
price: '100',
currency: 'GBP'
},
createdBy: {
username: 'user1',
name: 'Name 1',
date: '2022-04-28T22:41:59',
},
},
{
id: '122',
itemDate: '2022-04-28',
itemName: 'testname2',
itemCategory: 'Category B',
itemPrice: {
price: '100',
currency: 'GBP'
},
createdBy: {
username: 'user2',
name: 'Name 2',
date: '2022-04-28T22:42:44',
},
},
{
id: '122',
itemDate: '2022-04-28',
itemName: 'testname3',
itemCategory: 'Category C',
itemPrice: {
price: '200',
currency: 'GBP'
},
createdBy: {
username: 'user2',
name: 'Name 2',
date: '2022-04-28T22:43:16',
},
},
];
console.log('group and add prices per user: ', groupAndAdd(items));
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added to the snippet above.
PS: If you'd like to add value to stackoverflow community,
Please consider reading: What to do when my question is answered
Thank you !
For your first question, you're initialising correctly as an array, but you're using just object. Two ways you can do this.
First Option
let something = items.reduce(function(c, x) {
if (!c[x.createdBy.username])
c[x.createdBy.username] = {
username: x.createdBy.username,
total: 0,
}
c[x.createdBy.username].total += Number(x.itemPrice.price)
return c
}, {});
something = Object.values(something);
Second Option
I was thinking of using just push, but seems it's not possible, so the above is the only option.
Using push is possible, but it'll get too complicated by checking with find and updating the correct array element.
For your second question of summing up all the totals, you can use the simple syntax of:
const sum = arr.reduce((a, c) => a + c, 0);
This is the minimum code you need for array of numbers to be summed.

How would you change values/add to nested object data inside array of objects using Javascript?

const beers = [
{
id: '100',
name: 'stoneys'
},
{
id: '200',
name: 'budweiser'
},
{
id: '300',
name: 'miller'
},
{
id: '400',
name: 'corona'
}
];
const people = [
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: ['100']
},
{
name: 'penguins',
beers: ['300']
}
]
},
{
name: 'jim',
teams: [
{
name: 'indians',
beers: ['200']
},
{
name: 'blue jackets',
beers: ['100', '400']
}
]
}
];
let newPeople = people.map(fan => {
fan.teams.map(team => {
team.beers.map(beer => beers.filter(brand => brand.id === beer)[0])
});
});
Above is a sample I put together to best demonstrate my question. I am having trouble understanding why nested mapping (.map()) of object arrays is not allowing me to alter the nested data. When I console log results, I am either getting an "[undefined, undefined]' or the unchanged "people" array.
I would like to return the same array as "people" except replace the nested "beers" array (people.teams.beers[]) with corresponding objects from the "beers" array. Example of a successful result below:
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: [
{
id: '100',
name: 'stoneys'
}
]
},
{
name: 'penguins',
beers: [
{
id: '300',
name: 'miller'
}
]
}
]
}
Array.map expects a function which takes single array element as parameter and returns a mapped value. In your case you're not returning any value from mapping functions therefore you're getting undefined twice
const beers = [
{
id: '100',
name: 'stoneys'
},
{
id: '200',
name: 'budweiser'
},
{
id: '300',
name: 'miller'
},
{
id: '400',
name: 'corona'
}
];
const people = [
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: ['100']
},
{
name: 'penguins',
beers: ['300']
}
]
},
{
name: 'jim',
teams: [
{
name: 'indians',
beers: ['200']
},
{
name: 'blue jackets',
beers: ['100', '400']
}
]
}
];
let newPeople = people.map(fan => {
let teams = fan.teams.map(team => {
let beer = team.beers.map(beer => beers.filter(brand => brand.id === beer)[0]);
return { name: team.name, beers: beer }
});
return { name: fan.name, teams: teams }
});
console.log(newPeople);

Accessing nested array in a json

let ages = data
.filter(isDog)
.map(dogYears)
.reduce(sum);
mL/hr
i want to find the best way of accessing array elements in a javascript object.
Eg: I want to find the first faculty name & first specializations for each course.
var students =
{
deptartment:[
{
name:'Computer Science',
age:20,
Course:[
{ id: 100000
name:'Object Oriented Programming',
faculty:[
{
id:123,
name:'John',
Specialization: [
{name: 'science'},
{name: 'Physics'}
]
}
]
},
{ id: 100001
name:'C#',
faculty:[
{
id:124,
name:'Denis',
Specialization: [
{name: 'Ecnonomics'},
{name: 'Physics'}
]
}
]
}
],
}
]
};
I know i can get the faculty name and specialization by
var courses= deptartment && deptartment.Course ;
var facultyWithSpecialization= {};
if(courses){
courses.forEach(course =>{
var fname = course.faculty && course.faculty[0].name;
var s= course.faculty && course.faculty.Specialization;
facultyWithSpecialization[fname] = s && s[0].name;
})
}
use Object.assign({}, deptartment.Course) instead of department.Course
tried to use the below code but it doesn't make much difference.
var courses=Object.values(Object.assign({}, deptartment.Course));
var fname = Object.values(Object.assign({}, course.faculty[0].Specialization[0]));
Expecting
'John': 'science'
'Denis': 'Ecnonomics'
You can try this. There were many error in the object including spelling mistakes and formatting
var students = {
deptartment: [{
name: 'Computer Science',
age: 20,
Course: [{
id: 100000,
name: 'Object Oriented Programming',
faculty: [{
id: 123,
name: 'John',
Specialization: [{
name: 'science'
},
{
name: 'Physics'
}
]
},
{
id: 124,
name: 'Denis',
Specialization: [{
name: 'Ecnonomics'
},
{
name: 'Physics'
}
]
}
]
}],
}]
}
var obj = {};
students.deptartment.forEach((e) => {
e.Course.forEach((k) => {
k.faculty.forEach((l) => {
obj[l.name] = l.Specialization[0].name
})
})
})
console.log(obj)
I think you meant department instead of deptartment.
I modified a bit your JSON as it was a bit buggy:
var students = {
departments:[
{
name:'Computer Science',
age:20,
Courses:[
{ id: 100000,
name:'Object Oriented Programming',
faculty:[
{
id:123,
name:'John',
Specialization: [
{name: 'science'},
{name: 'Physics'}
]
},
{
id:124,
name:'Denis',
Specialization: [
{name: 'Ecnonomics'},
{name: 'Physics'}
]
}
]
}
],
}]
}
You can use map to achieve this nesting:
students.departments.map(
department => department.Courses.map(
course => course.faculty.map(
student => ({
name: student.name,
specialization: student.Specialization[0].name // check nulls here!
})
)
)
)

Iterating on nested arrays in objects to collate informaton

I have the following array of objects:
let array = [
{key: 'Newcastle', values[
{key: 'ID1000', values[
{name: 'Jeff', cust_id: "ID1000", status: 'sold'},
{name: 'Jeff', cust_id: "ID1000", status: 'pending'},
]}
{key: 'ID2000', values [
{name: 'Bob', cust_id: "ID2000", status: 'sold'}
]}
]}
{key: 'London', values [
{key: 'ID3000', values[
{name: 'Gary', cust_id: "ID3000", status: 'sold'},
]}
{key: 'ID4000', values[
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'pending'},
]}
]}
]
I have been trying to refactor it into something like:
[
{Location: 'Newcastle, customers: 2, sold: 2, pending: 1, interest: 0},
{Location: 'London', customers: 2, sold: 1, pending: 1, interest: 2}
]
So I am attempting to count the number of status events and collate them accordingly.
I get lost when I try to iterate on the nested arrays and then when trying to bubble up the results of the iteration to a final object. The closest I have got is:
function transform(array) {
let arr = []
array.forEach(function(x) {
function soldCount() {
x.values.forEach(function(x) {
let sold = x.values.forEach(function(x) {
let soldTrue = 0
if (x.status === "sold") {
soldTrue++
}
console.log(soldTrue)
if (soldTrue > 0) {
return soldTrue
}
})
})
}
let obj = {
location: x.key,
customers: x.values.length,
sold: soldCount()
}
arr.push(obj)
})
return arr
}
This tries to iterate on each array in the objects and attempts to return a number for how many of the 'sold' status it finds. The console statement does return a number but it returns multiple entries for each item in the array due to 'forEach'.
I am swamped in a number of forEach loops iterating on nested arrays. I suspect that this might not be the correct methodology for what I am trying to achieve.
You could take an object counting status and build a new objects.
var array = [{ key: 'Newcastle', values: [{ key: 'ID1000', values: [{ name: 'Jeff', cust_id: "ID1000", status: 'sold' }, { name: 'Jeff', cust_id: "ID1000", status: 'pending' },] }, { key: 'ID2000', values: [{ name: 'Bob', cust_id: "ID2000", status: 'sold' }] }] }, { key: 'London', values: [{ key: 'ID3000', values: [{ name: 'Gary', cust_id: "ID3000", status: 'sold' },] }, { key: 'ID4000', values: [{ name: 'Mary', cust_id: "ID4000", status: 'interest' }, { name: 'Mary', cust_id: "ID4000", status: 'interest' }, { name: 'Mary', cust_id: "ID4000", status: 'pending' }] }] }],
result = array.map(({ key: Location, values }) => {
var data = { Location, customers: 0, sold: 0, pending: 0, interest: 0 };
values.forEach(({ values }) => {
data.customers++;
values.forEach(({ status }) => data[status]++);
});
return data;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Look at this:
let array = [
{
key: 'Newcastle',
values: [
{key: 'ID1000',
values:[
{name: 'Jeff', cust_id: "ID1000", status: 'sold'},
{name: 'Jeff', cust_id: "ID1000", status: 'pending'},
]},
{
key: 'ID2000',
values :[
{name: 'Bob', cust_id: "ID2000", status: 'sold'}
]
}
]},
{key: 'London', values :[
{key: 'ID3000', values:[
{name: 'Gary', cust_id: "ID3000", status: 'sold'},
]},
{key: 'ID4000', values:[
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'pending'},
]}
]}
]
function countProp(arr, prop) {
let n = 0;
arr.forEach((items) => {
items.values.forEach(item => {
if (item.status === prop) {
n += 1;
}
})
});
return n;
}
const n = array.map(item => {
const a = {};
a.customers = item.values.length;
a.location = item.key;
a.sold = countProp(item.values, "sold");
a.pending = countProp(item.values, "pending");
a.interest = countProp(item.values, "interest");
return a;
});
This gives
[
{Location: 'Newcastle, customers: 2, sold: 2, pending: 1, interest: 0},
{Location: 'London', customers: 2, sold: 1, pending: 1, interest: 2}
]
Assumingly you are using let.
I have written my solution in ES6, getting Location, customer count is easy.
The challenging part would be the accumulating the status count per type of status, you can use reduce with object as initial value and using the status types as keys with 0 initial value, which shown in my solution
let array = [
{key: 'Newcastle', values: [
{key: 'ID1000', values: [
{name: 'Jeff', cust_id: "ID1000", status: 'sold'},
{name: 'Jeff', cust_id: "ID1000", status: 'pending'}
]},
{key: 'ID2000', values: [
{name: 'Bob', cust_id: "ID2000", status: 'sold'}
]}
]},
{key: 'London', values: [
{key: 'ID3000', values: [
{name: 'Gary', cust_id: "ID3000", status: 'sold'}
]},
{key: 'ID4000', values:[
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'pending'}
]}
]}
];
const newArray = array.map((a) => {
const Location = a.key;
const customer = a.values.length;
const status = a.values.reduce((acc, value) => {
value.values.forEach((v1) => {
acc[v1.status] = acc[v1.status] + 1;
});
return acc;
}, {sold: 0, pending: 0, interest: 0});
return {Location, customer, ...status};
});
console.log(newArray);

Categories