Mapping through nested arrays in es6 javascript - reactjs

I have a simple array that I want to group it's objects by date so I used this function
const groupedDates= Object.entries(
items.reduce((acc, { product, price, type, date }) => {
if (!acc[date]) {
acc[date] = [];
}
acc[date].push({ product, price, type date });
return acc;
}, {})
).map(([date, items]) => ({ date, items }));
the array
const items = [
{
id: 1,
product: "milk",
price: 10,
type: "drink"
date: "01/01/2022",
},
{
id: 2,
product: "coca",
price: 11,
type: "drink"
date: "01/01/2022",
},
{
id: 3,
product: "pepsi",
price: 20,
type: "drink"
date: "01/01/2024",
},
{
id: 4,
product: "carrots",
price: 30,
type: "food",
date: "01/01/2023",
},
];
I got this result
{
0: [
date: 01/01/2022,
items : [
0 : {
id: 1,
product: "milk",
price: 10,
type: "drink"
date: "01/01/2022"
}
1 : {
id: 2,
product: "coca",
price: 11,
type: "drink"
date: "01/01/2022",
}
],
1: [
date: "01/01/2024",
items : [
0 : {
id: 3,
product: "pepsi",
price: 20,
type: "drink"
date: "01/01/2024",
}
],
2: [
date: "01/01/2023",
items: [
0:{
id: 4,
product: "carrots",
price: 30,
type: "food",
date: "01/01/2023"
}
]
]
}
Issue:
I cannot seem to figure out how to access items1 when it exists.
What I have tried
is the map below but it only returns the first level of items which is 0 and if I do items1 it returns an error because not all arrays have a second item.
{groupedDates.map((obj) => (
{obj.items[0].product}))}
UPDATE
I'd also like to get the total for each date so I can have a card that has the Date + The total + each item and it's individual price. After getting some help from #Nick, I've managed to output the date, the item and it's price, now I'm still missing the total price for the date.

You need to iterate the items in each obj to get the list of products:
const items = [
{ id: 1, product: "milk", price: 10, type: "drink", date: "01/01/2022" },
{ id: 2, product: "coca", price: 11, type: "drink", date: "01/01/2022" },
{ id: 3, product: "pepsi", price: 20, type: "drink", date: "01/01/2024" },
{ id: 4, product: "carrots", price: 30, type: "food", date: "01/01/2023" },
];
const groupedDates = Object.entries(
items.reduce((acc, { product, price, type, date }) => {
if (!acc[date]) {
acc[date] = [];
}
acc[date].push({ product, price, type, date });
return acc;
}, {})
).map(([date, items]) => ({ date, items }));
const allProducts = groupedDates.map((obj) => obj.items.map(i => i.product))
console.log(allProducts)
const totalsByDate = groupedDates.map(({ date, items }) => (
{ [date] : items.reduce((acc, item) => acc + item.price, 0) }
))
console.log(totalsByDate)
.as-console-wrapper { max-height:100% !important; top 0 }
Note I would make groupedDates an object with its keys being the dates; that will make looking up data for a given date much easier. For example:
const items = [
{ id: 1, product: "milk", price: 10, type: "drink", date: "01/01/2022" },
{ id: 2, product: "coca", price: 11, type: "drink", date: "01/01/2022" },
{ id: 3, product: "pepsi", price: 20, type: "drink", date: "01/01/2024" },
{ id: 4, product: "carrots", price: 30, type: "food", date: "01/01/2023" },
];
const groupedDates = items.reduce((acc, { date, ...rest }) => {
acc[date] = (acc[date] || []).concat({ ...rest })
return acc;
}, {})
console.log(groupedDates)
const allProducts = Object.values(groupedDates)
.flatMap(arr => arr.map(obj => obj.product))
console.log(allProducts)
const totalsByDate = Object.entries(groupedDates).map(([ date, items ]) => (
{ [date] : items.reduce((acc, item) => acc + item.price, 0) }
))
console.log(totalsByDate)
.as-console-wrapper { max-height:100% !important; top 0; }

Related

Remap structure parent object base on child array data and length object

I am trying remap structure data parent base on child array data and length. It's possible to do? Structure array it's good or i need change array object from backend?
There is no id in child arrays.
This is what i have done
this.arrays = [
{
id: '1',
idbid: '0000000001618',
name: 'ebi',
rate: '400',
bid: [
{
bid: 10000,
date: '2022/12/12',
value: 5000,
},
{
bid: 10000,
date: '2022/12/14',
value: 8100,
},
{
bid: 15000,
date: '2022/12/15',
value: 8100,
},
],
},
{
id: '2',
idbid: '0000000001618',
name: 'bio',
rate: '100',
bid: [
{
bid: 8000,
date: '2022/12/13',
value: 8000,
},
],
},
];
// merge all item bid in child array
let allVal: any = [];
allVal = allVal.concat(this.arrays.map((data) => data.bid).flat());
console.log(allVal);
// get unique bid
var uniqueData = [];
allVal.forEach((item) => {
let count = uniqueData.filter((x) => x.value == item.value).length;
if (count == 0) {
uniqueData.push(item);
}
});
console.log(uniqueData);
// find and merge into parent array
const newArrays = uniqueData.map((obj) => {
return this.arrays.find((data) =>
data.bid.some((val) => val.value == obj.value)
);
});
console.log(newArrays);
// remap structure custom arrays of parent
const remapArrays = newArrays.map((obj, index) => {
return {
id: index + 1,
idbid: obj.idbid,
name: obj.name,
rate: obj.rate,
bid: obj.bid[index]?.bid,
date: obj.bid[index]?.date,
value: obj.bid[index]?.value,
};
});
console.log(remapArrays);
but the result is like this
[
{
id: '1',
idbid: '0000000001618',
name: 'ebi',
rate: '400',
bid: 10000,
date: '2022/12/12',
value: 5000,
},
{
id: '2',
idbid: '0000000001618',
name: 'bio',
rate: '100',
bid: 10000,
date: '2022/12/13',
value: 8100,
},
{
id: '3',
idbid: '0000000001618',
name: 'ebi',
rate: '400',
bid: undefined,
date: undefined,
value: undefined,
},
];
and here is what expected output
// output final that exptected
this.customArrays = [
{
id: '1',
idbid: '0000000001618',
name: 'ebi',
rate: '400',
bid: 10000,
date: '2022/12/12',
value: 5000,
},
{
id: '2',
idbid: '0000000001618',
name: 'bio',
rate: '100',
bid: 8000,
date: '2022/12/13',
value: 8000,
},
{
id: '3',
idbid: '0000000001618',
name: 'ebi',
rate: '400',
bid: 15000,
date: '2022/12/15',
value: 8100,
},
];
Here is link for test Stackblitz
There are some things that should be defined more clearly in the question:
Do we expect the value of "idbid" to be the same across all elements of the original array?
Should the resulting array have the first date of each unique bid?
In my answer, I'll be assuming that the answer is "yes" to both.
In the beginning, when merging all child arrays:
allVal.concat(this.arrays.map((data) => data.bid).flat());
We're losing the values that are unique to each parent element: for example, the value of "rate" is "400" for the first parent element and "100" for the second. So before flattening, we need to map each of the bid arrays, so each child element includes the data from the parent element:
// merge all item bids in child arrays
const allVal = this.arrays
.map((dataWithBids) => {
const { id, idbid, name, rate, bid: bidArr } = dataWithBids;
// add data from the parent element to each bid
return bidArr.map((bidData) => ({
idbid,
name,
rate,
...bidData,
}));
})
.flat();
In the following step, getting only unique values can be done the same way as you did, or using the array some method:
// get unique bids
const uniqueData = [];
allVal.forEach((item) => {
const exists = uniqueData.some((x) => x.value == item.value);
if (!exists) {
uniqueData.push(item);
}
});
At this point we have the correct values, and we need to order them by date and add ids:
const sorted = uniqueData
// sort by date
.sort(
(bidData1, bidData2) =>
Date.parse(bidData1.date) - Date.parse(bidData2.date)
)
// add ids
.map((bidData, index) => ({
...bidData,
id: index + 1 // or (index + 1).toString(),
}));
The result of the above sorted array matches the expected output.

I am trying to get a function to search an array of objects and then return the entire object based on the filter

I am trying to get my function working and I can not for the life of me figure out why it isnt. I am trying to use .filter() to search the array of objects to find the object with the tag ketchup. Then return the whole object it is in to the console log
let foodArr = [
{
type: 'Chicken',
rating: 1,
tags: ['chicken', 'free-range', 'no hormones'],
price: 10,
popularity: 80
},
{
type: 'pizza',
rating: 5,
tags: ['pepperoni', 'sauce', 'bread'],
price: 25,
popularity: 56
},
{
type: 'hamburger',
rating: 3,
tags: ['bun', 'patty', 'lettuce'],
price: 8,
popularity: 99
},
{
type: 'wings',
rating: 4,
tags: ['wing', 'bbq', 'ranch'],
price: 12,
popularity: 68
},
{
type: 'fries',
rating: 2,
tags: ['ketchup'],
price: 4,
popularity: 100
}
]
const filteredFood = foodArr.filter(function(obj) {
return obj.tags[''] === 'ketchup'
})
console.log(filteredFood)
const filteredFood = foodArr.filter(function(value){
for (let i=0; i<value.tags.length; i++)
if(value.tags[i] === 'ranch'){
return value.tags[i]
}
}
)
console.log(filteredFood)

TypeError: Cannot destructure property 'title' of 'collections' as it is undefined

I am trying to get collections using selectors. Here is my code:
Selectors
import { createSelector } from "reselect";
export const selectShop = state => state.shop;
export const selectCollections = createSelector(
[selectShop],
shop => shop.collections
)
export const selectCollectionForPreview = createSelector(
[selectCollections],
collections => Object.keys(collections).map(key => collections[key])
)
export const selectCollectionItem = collectionUrl =>
createSelector(
[selectCollections],
collections => collections[collectionUrl]
//It seems that the selector above is returning collections as an array instead of object, so collections[collectionUrl] is returning undefined
)
Component
const CollectionPage = ({collections}) => {
// const {title, items} = Object.keys(collections)
const {title, items} = collections
console.log('collection', collections);
return(
// <></>
<div className="collection-page">
<h2 className='title'>{title}</h2>
<div className='items'>
{items.map( item =>
<CollectionItem key={item.id} item={item}/>
)}
</div>
</div>
)
}
const mapStateToProps = (state,ownProps) => ({
collections: selectCollectionItem(ownProps.match.params.collectionId)(state)
})
export default connect(mapStateToProps)(CollectionPage);
SHOP_DATA
const SHOP_DATA = {
hats:{
id: 1,
title: 'Hats',
routeName: 'hats',
items: [
{
id: 1,
name: 'Brown Brim',
imageUrl: 'https://i.ibb.co/ZYW3VTp/brown-brim.png',
price: 25
},
{
id: 2,
name: 'Blue Beanie',
imageUrl: 'https://i.ibb.co/ypkgK0X/blue-beanie.png',
price: 18
},
{
id: 3,
name: 'Brown Cowboy',
imageUrl: 'https://i.ibb.co/QdJwgmp/brown-cowboy.png',
price: 35
},
{
id: 4,
name: 'Grey Brim',
imageUrl: 'https://i.ibb.co/RjBLWxB/grey-brim.png',
price: 25
},
{
id: 5,
name: 'Green Beanie',
imageUrl: 'https://i.ibb.co/YTjW3vF/green-beanie.png',
price: 18
},
{
id: 6,
name: 'Palm Tree Cap',
imageUrl: 'https://i.ibb.co/rKBDvJX/palm-tree-cap.png',
price: 14
},
{
id: 7,
name: 'Red Beanie',
imageUrl: 'https://i.ibb.co/bLB646Z/red-beanie.png',
price: 18
},
{
id: 8,
name: 'Wolf Cap',
imageUrl: 'https://i.ibb.co/1f2nWMM/wolf-cap.png',
price: 14
},
{
id: 9,
name: 'Blue Snapback',
imageUrl: 'https://i.ibb.co/X2VJP2W/blue-snapback.png',
price: 16
}
]
},
sneakers:{
id: 2,
title: 'Sneakers',
routeName: 'sneakers',
items: [
{
id: 10,
name: 'Adidas NMD',
imageUrl: 'https://i.ibb.co/0s3pdnc/adidas-nmd.png',
price: 220
},
{
id: 11,
name: 'Adidas Yeezy',
imageUrl: 'https://i.ibb.co/dJbG1cT/yeezy.png',
price: 280
},
{
id: 12,
name: 'Black Converse',
imageUrl: 'https://i.ibb.co/bPmVXyP/black-converse.png',
price: 110
},
{
id: 13,
name: 'Nike White AirForce',
imageUrl: 'https://i.ibb.co/1RcFPk0/white-nike-high-tops.png',
price: 160
},
{
id: 14,
name: 'Nike Red High Tops',
imageUrl: 'https://i.ibb.co/QcvzydB/nikes-red.png',
price: 160
},
{
id: 15,
name: 'Nike Brown High Tops',
imageUrl: 'https://i.ibb.co/fMTV342/nike-brown.png',
price: 160
},
{
id: 16,
name: 'Air Jordan Limited',
imageUrl: 'https://i.ibb.co/w4k6Ws9/nike-funky.png',
price: 190
},
{
id: 17,
name: 'Timberlands',
imageUrl: 'https://i.ibb.co/Mhh6wBg/timberlands.png',
price: 200
}
]
},
jackets:{
id: 3,
title: 'Jackets',
routeName: 'jackets',
items: [
{
id: 18,
name: 'Black Jean Shearling',
imageUrl: 'https://i.ibb.co/XzcwL5s/black-shearling.png',
price: 125
},
{
id: 19,
name: 'Blue Jean Jacket',
imageUrl: 'https://i.ibb.co/mJS6vz0/blue-jean-jacket.png',
price: 90
},
{
id: 20,
name: 'Grey Jean Jacket',
imageUrl: 'https://i.ibb.co/N71k1ML/grey-jean-jacket.png',
price: 90
},
{
id: 21,
name: 'Brown Shearling',
imageUrl: 'https://i.ibb.co/s96FpdP/brown-shearling.png',
price: 165
},
{
id: 22,
name: 'Tan Trench',
imageUrl: 'https://i.ibb.co/M6hHc3F/brown-trench.png',
price: 185
}
]
},
womens:{
id: 4,
title: 'Womens',
routeName: 'womens',
items: [
{
id: 23,
name: 'Blue Tanktop',
imageUrl: 'https://i.ibb.co/7CQVJNm/blue-tank.png',
price: 25
},
{
id: 24,
name: 'Floral Blouse',
imageUrl: 'https://i.ibb.co/4W2DGKm/floral-blouse.png',
price: 20
},
{
id: 25,
name: 'Floral Dress',
imageUrl: 'https://i.ibb.co/KV18Ysr/floral-skirt.png',
price: 80
},
{
id: 26,
name: 'Red Dots Dress',
imageUrl: 'https://i.ibb.co/N3BN1bh/red-polka-dot-dress.png',
price: 80
},
{
id: 27,
name: 'Striped Sweater',
imageUrl: 'https://i.ibb.co/KmSkMbH/striped-sweater.png',
price: 45
},
{
id: 28,
name: 'Yellow Track Suit',
imageUrl: 'https://i.ibb.co/v1cvwNf/yellow-track-suit.png',
price: 135
},
{
id: 29,
name: 'White Blouse',
imageUrl: 'https://i.ibb.co/qBcrsJg/white-vest.png',
price: 20
}
]
},
mens:{
id: 5,
title: 'Mens',
routeName: 'mens',
items: [
{
id: 30,
name: 'Camo Down Vest',
imageUrl: 'https://i.ibb.co/xJS0T3Y/camo-vest.png',
price: 325
},
{
id: 31,
name: 'Floral T-shirt',
imageUrl: 'https://i.ibb.co/qMQ75QZ/floral-shirt.png',
price: 20
},
{
id: 32,
name: 'Black & White Longsleeve',
imageUrl: 'https://i.ibb.co/55z32tw/long-sleeve.png',
price: 25
},
{
id: 33,
name: 'Pink T-shirt',
imageUrl: 'https://i.ibb.co/RvwnBL8/pink-shirt.png',
price: 25
},
{
id: 34,
name: 'Jean Long Sleeve',
imageUrl: 'https://i.ibb.co/VpW4x5t/roll-up-jean-shirt.png',
price: 40
},
{
id: 35,
name: 'Burgundy T-shirt',
imageUrl: 'https://i.ibb.co/mh3VM1f/polka-dot-shirt.png',
price: 25
}
]
}
};
export default SHOP_DATA;
Please provide any pointers or advice on what am I doing wrong? I keep getting the error. It seems that the collection is undefined. I don't understand why. Ideally it should return an object.
You are exporting the component like this
const mapStateToProps = (state,ownProps) => ({
collections: selectCollectionItem(ownProps.match.params.collectionId)(state)
})
export default connect(mapStateToProps)(CollectionPage);
The ownProps.match.params.collectionId might be undefined.
Add this at the top of your file
import { withRouter} from 'react-router-dom'
and export your component like
export default connect(mapStateToProps)(withRouter(CollectionPage));

How to group array and count values

I have an array that I want to group according to type then count the number of instances for every unique status.
Here is my array:
const array = [
{ type: 'A', number: 2, status:'Approved', year: '2020' },
{ type: 'A', number: 3, status:'Approved', year: '2020' },
{ type: 'A', number: 3, status:'Disapproved', year: '2020' },
{ type: 'A', number: 6, status:'Disapproved', year: '2020' },
{ type: 'A', number: 5, status:'Processing', year: '2020' },
{ type: 'B', number: 8, status:'Processing', year: '2020' },
{ type: 'B', number: 2, status:'Approved', year: '2020' },
{ type: 'B', number: 2, status:'Disapproved', year: '2020' },
]
Here is my desired array:
const desiredArray = [
{ type: 'A', Approved:5, Disapproved: 9, Processing: 13, year: '2020' },
{ type: 'B', Approved:2, Disapproved: 2, year: '2020' },
]
With help from other online friends, this worked perfectly:
const reducedObject = array.reduce((rv,x) =>{
if(!rv[x.type]) {
rv[x.type] = {
type: x.type,
Approved: 0,
Disapproved: 0,
Processing: 0,
year: x.year
}
}
rv[x.type][x.status]++;
return rv;
}, {});
const desiredArray = Object.values(reducedObject);
Another answer:
var array = [
{ Id: "001", qty: 1 },
{ Id: "002", qty: 2 },
{ Id: "001", qty: 2 },
{ Id: "003", qty: 4 }
];
var result = [];
array.reduce(function(res, value) {
if (!res[value.Id]) {
res[value.Id] = { Id: value.Id, qty: 0 };
result.push(res[value.Id])
}
res[value.Id].qty += value.qty;
return res;
}, {});
console.log(result)

How to get total of numbers in an array from SharePoint list items

I'm creating a spfx web part using Reactjs. I have a function getting an array of items from a SharePoint list that includes a number column of "Hours". I need to get a total for all the hours that have been returned but can't figure out how to calculate that.
I feel like I'm missing something simple but I've run through all kinds of loops and for some reason I can't get it to work. I've verified that I am getting data from the Hours column.
I'll also state the obligatory "I'm new to spfx and react". :) TIA for any help!
private readItem(): void {
this.props.spHttpClient.get(`${this.props.siteUrl}/_api/web/lists/getbytitle('Time Off')/items?$select=Title,Id,Hours`,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
}
}).then((response: SPHttpClientResponse): Promise<ITimeOffItem[]> => {
return response.json();
})
.then((item: ITimeOffItem[]): void => {
console.log(item); //the data is here including Hours
this.setState({
items: item,
hoursTotal: //????How do I get the sum of "Hours" and assign it to a number in state
});
});
}
Create a function to loop through the items and add the hours
function countHours(items) {
if (!items) {
return 0;
}
let total = 0;
for (let i = 0; i < items.length; i++) {
total += items[i].Hours;
}
return total;
}
const item = [
{ Id: 25, Title: "Comp Time", Hours: 6, ID: 25 },
{ Id: 26, Title: "Comp Time", Hours: 5, ID: 26 },
{ Id: 27, Title: "Work from Home", Hours: 3, ID: 27 },
{ Id: 28, Title: "Comp Time", Hours: 7, ID: 28 },
{ Id: 29, Title: "Work from Home", Hours: 8, ID: 29 },
{ Id: 30, Title: "Holiday", Hours: 8, ID: 30 },
{ Id: 31, Title: "Work from Home", Hours: 32, ID: 31 }
];
console.log(countHours(item));
Use it like
this.setState({
items: item,
hoursTotal: countHours(item)
});
you can also use reduce
const item = [
{ Id: 25, Title: "Comp Time", Hours: 6, ID: 25 },
{ Id: 26, Title: "Comp Time", Hours: 5, ID: 26 },
{ Id: 27, Title: "Work from Home", Hours: 3, ID: 27 },
{ Id: 28, Title: "Comp Time", Hours: 7, ID: 28 },
{ Id: 29, Title: "Work from Home", Hours: 8, ID: 29 },
{ Id: 30, Title: "Holiday", Hours: 8, ID: 30 },
{ Id: 31, Title: "Work from Home", Hours: 32, ID: 31 }
];
const sum = item.reduce(function(a, b) { return a + b.Hours; }, 0);
console.log(sum)
It's hard to answer this without knowing what the data structure looks like, but if you are trying to sum an array of numbers you could use reduce.
const hours = [7, 5, 3, 1, 7]
const totalHours = hours.reduce((accumulator, hour) => accumulator + hour)

Resources