Sum of specific field in one array if condition is met - javascript

I have two arrays. I would like to get the sum of time_spent field if course_id is same in arr1 as well as if course_id matches the id field of arr2
let arr1 = [
{ instructor_id: 7, course_id: 19, lesson_id: 1, time_spent: 0 },
{ instructor_id: 7, course_id: 19, lesson_id: 2, time_spent: 0 },
{ instructor_id: 7, course_id: 19, lesson_id: 3, time_spent: 0 },
{ instructor_id: 7, course_id: 20, lesson_id: 4, time_spent: 80 },
{ instructor_id: 7, course_id: 20, lesson_id: 5, time_spent: 40 },
{ instructor_id: 8, course_id: 21, lesson_id: 6, time_spent: 0 },
];
let arr2 = [
{ id: 19, title: "Course 19", duration: 180 },
{ id: 20, title: "Course 20", duration: 120 },
];
// expected result
newArr = [
{ instructor_id: 7, course_id: 19, time_spent: 0 },
{ instructor_id: 7, course_id: 20, time_spent: 120 },
];

There are many ways to do this, and although you didn't fully specify what should happen with all the fields, there is a straightforward way to use Array reduce to perform a selective sum.
As a clever shortcut, you can multiply a value by a boolean to sum up only matching values by adding 0 for non-matching items. condition * value is equivalent to the ternary condition ? value : 0
const arr1 = [
{ instructor_id: 7, course_id: 19, lesson_id: 1, time_spent: 0 },
{ instructor_id: 7, course_id: 19, lesson_id: 2, time_spent: 0 },
{ instructor_id: 7, course_id: 19, lesson_id: 3, time_spent: 0 },
{ instructor_id: 7, course_id: 20, lesson_id: 4, time_spent: 80 },
{ instructor_id: 7, course_id: 20, lesson_id: 5, time_spent: 40 },
{ instructor_id: 8, course_id: 21, lesson_id: 6, time_spent: 0 },
];
const arr2 = [
{ id: 19, title: "Course 19", duration: 180 },
{ id: 20, title: "Course 20", duration: 120 },
];
const result = arr2.map(({ id }) => ({
course_id: id,
time_spent: arr1.reduce(
(prev, cur, index) => prev + (cur.course_id == id) * cur.time_spent
, 0)
}));
console.log(result);

Related

How to reduce the array of objects to order it and remove unwanted nesting?

I'm having some problems with returning a set of data.
The data comes in a form of TourStation > Station > TourStation > Station > TourStation > Station
TourStation stores info about the connection and nested stations, Station contains info about the station and its children
But I need it in a form of Station > Station > Station (Station with station children, 2 levels deep)
Here is the data in question:
//Data structure
const DATA = [
{
id: 47,
tourId: 37,
stationId: 7,
parentStationId: null,
order: 0,
station: {
id: 7,
textCode: "1234",
published: true,
subjectId: 2,
created: "2022-07-12T19:01:17.049Z",
updated: "2022-07-15T11:36:46.195Z",
expired: null,
children: []
}
},
{
id: 50,
tourId: 37,
stationId: 9,
parentStationId: null,
order: 1,
station: {
id: 9,
textCode: "asdd",
published: true,
subjectId: 2,
created: "2022-07-15T13:47:55.557Z",
updated: "2022-08-10T14:32:35.528Z",
expired: null,
children: [
{
id: 51,
tourId: 37,
stationId: 10,
parentStationId: 9,
order: 0,
station: {
id: 10,
textCode: "123",
published: true,
subjectId: 2,
created: "2022-07-25T11:49:21.688Z",
updated: "2022-07-25T11:50:25.445Z",
expired: null,
children: []
}
},
{
id: 48,
tourId: 37,
stationId: 11,
parentStationId: 9,
order: 1,
station: {
id: 11,
textCode: "sada",
published: true,
subjectId: 2,
created: "2022-07-25T11:50:46.021Z",
updated: "2022-07-25T11:50:48.567Z",
expired: null,
children: [
{
id: 49,
tourId: 37,
stationId: 12,
parentStationId: 11,
order: 0,
station: {
id: 12,
textCode: "ASD",
published: true,
subjectId: 2,
created: "2022-08-10T11:07:38.790Z",
updated: "2023-01-20T12:44:59.925Z",
expired: null
}
}
]
}
}
]
}
}
];
And this is the expected result after reducing it
// expected result, ordered by "order" variable from above
const dataExpected = [
{
id: 7,
textCode: "1234",
published: true,
subjectId: 2,
created: "2022-07-12T19:01:17.049Z",
updated: "2022-07-15T11:36:46.195Z",
expired: null,
children: []
},
{
id: 9,
textCode: "asdd",
published: true,
subjectId: 2,
created: "2022-07-15T13:47:55.557Z",
updated: "2022-08-10T14:32:35.528Z",
expired: null,
children: [
{
id: 10,
textCode: "123",
published: true,
subjectId: 2,
created: "2022-07-25T11:49:21.688Z",
updated: "2022-07-25T11:50:25.445Z",
expired: null,
children: []
},
{
id: 11,
textCode: "sada",
published: true,
subjectId: 2,
created: "2022-07-25T11:50:46.021Z",
updated: "2022-07-25T11:50:48.567Z",
expired: null,
children: [
{
id: 12,
textCode: "ASD",
published: true,
subjectId: 2,
created: "2022-08-10T11:07:38.790Z",
updated: "2023-01-20T12:44:59.925Z",
expired: null
}
]
}
]
}
];
const dataToReturn = DATA.reduce((prev, curr, index, arr) => {
//Only get the station and its children, and order them by "order"
}, []);
console.log("RETURNED: ", JSON.stringify(dataToReturn, null, 2));
console.log("EXPECTED: ", JSON.stringify(dataExpected, null, 2));
Here is a codesandbox for this issue: https://codesandbox.io/s/sweet-forest-h23gsi?file=/src/index.js:300-3978
How can I use the reduce function, or something else to get this to work. If there was no nesting, a simple DATA.map(item => item.station) would work.
const data = [{"id":47,"tourId":37,"stationId":7,"parentStationId":null,"order":0,"station":{"id":7,"textCode":"1234","published":true,"subjectId":2,"created":"2022-07-12T19:01:17.049Z","updated":"2022-07-15T11:36:46.195Z","expired":null,"children":[]}},{"id":50,"tourId":37,"stationId":9,"parentStationId":null,"order":1,"station":{"id":9,"textCode":"asdd","published":true,"subjectId":2,"created":"2022-07-15T13:47:55.557Z","updated":"2022-08-10T14:32:35.528Z","expired":null,"children":[{"id":51,"tourId":37,"stationId":10,"parentStationId":9,"order":0,"station":{"id":10,"textCode":"123","published":true,"subjectId":2,"created":"2022-07-25T11:49:21.688Z","updated":"2022-07-25T11:50:25.445Z","expired":null,"children":[]}},{"id":48,"tourId":37,"stationId":11,"parentStationId":9,"order":1,"station":{"id":11,"textCode":"sada","published":true,"subjectId":2,"created":"2022-07-25T11:50:46.021Z","updated":"2022-07-25T11:50:48.567Z","expired":null,"children":[{"id":49,"tourId":37,"stationId":12,"parentStationId":11,"order":0,"station":{"id":12,"textCode":"ASD","published":true,"subjectId":2,"created":"2022-08-10T11:07:38.790Z","updated":"2023-01-20T12:44:59.925Z","expired":null}}]}}]}}]
const f = data =>
data
.sort((a,b)=>a.order-b.order)
.map(({station:{children=[], ...rest}})=>
({...rest, children:f(children)}))
const result = f(data)
console.log(result)
You just had to run map recursively:
const data = [{
id: 50,
tourId: 37,
stationId: 9,
parentStationId: null,
order: 1,
station: {
id: 9,
textCode: "asdd",
published: true,
subjectId: 2,
created: "2022-07-15T13:47:55.557Z",
updated: "2022-08-10T14:32:35.528Z",
expired: null,
children: [{
id: 51,
tourId: 37,
stationId: 10,
parentStationId: 9,
order: 0,
station: {
id: 10,
textCode: "123",
published: true,
subjectId: 2,
created: "2022-07-25T11:49:21.688Z",
updated: "2022-07-25T11:50:25.445Z",
expired: null,
children: []
}
},
{
id: 48,
tourId: 37,
stationId: 11,
parentStationId: 9,
order: 1,
station: {
id: 11,
textCode: "sada",
published: true,
subjectId: 2,
created: "2022-07-25T11:50:46.021Z",
updated: "2022-07-25T11:50:48.567Z",
expired: null,
children: [{
id: 49,
tourId: 37,
stationId: 12,
parentStationId: 11,
order: 0,
station: {
id: 12,
textCode: "ASD",
published: true,
subjectId: 2,
created: "2022-08-10T11:07:38.790Z",
updated: "2023-01-20T12:44:59.925Z",
expired: null
}
}]
}
}
]
}
}, {
id: 47,
tourId: 37,
stationId: 7,
parentStationId: null,
order: 0,
station: {
id: 7,
textCode: "1234",
published: true,
subjectId: 2,
created: "2022-07-12T19:01:17.049Z",
updated: "2022-07-15T11:36:46.195Z",
expired: null,
children: []
}
}
];
const leaveOnlyStations = (tourStations) =>
tourStations
.sort((a, b) => a.order - b.order)
.map(tourStation => {
const station = tourStation.station;
const subTourStations = station.children ?? [];
if (subTourStations.length === 0) return station;
station.children = leaveOnlyStations(subTourStations);
return station;
})
console.log(leaveOnlyStations(data));

why am i getting <1 empty item> when using mapping in JavaScript

im using JSON to return an array.
Json:
const data = [{
"week": 1,
"lost": 10,
"recovery_timespan": [{
"week": 2,
"count": 1
}, {
"week": 3,
"count": 0
}],
"netLost": 10,
"netReturned": 20
}, {
"week": 2,
"lost": 7,
"recovery_timespan": [{
"week": 3,
"count": 1
}, {
"week": 4,
"count": 3
}],
"netLost": 30,
"netReturned": 200
}, {
"week": 3,
"lost": 8,
"recovery_timespan": [{
"week": 4,
"count": 1
}],
"netLost": 50,
"netReturned": 40
}];
i need to get the data into a array with lost,counts of recovery_timespan,netLost,netReturned.
Expected Output:
[ [ 10, 1, 0, 10, 20 ],
[ 7, 1, 3, 30, 200 ],
[ 8, 1, 50, 40 ] ]
My approach:
const result = data.map(({lost, recovery_timespan,netLost,netReturned}) => [
lost,
...recovery_timespan.map(({count}) => count),
,netLost,netReturned
]);
console.log(result);
and this return array with <1 empty item>:
[ [ 10, 1, 0, <1 empty item>, 10, 20 ],
[ 7, 1, 3, <1 empty item>, 30, 200 ],
[ 8, 1, <1 empty item>, 50, 40 ] ]
Wha is the issue here?
Why am i getting <1 empty item>
You have an extra comma:
const result = data.map(({lost, recovery_timespan,netLost,netReturned}) => [
lost,
...recovery_timespan.map(({count}) => count),
here ---> ,netLost,netReturned
]);
Just remove it.
You have an additional , after the nested map:
const result = data.map(({lost, recovery_timespan,netLost,netReturned}) => [
lost,
...recovery_timespan.map(({count}) => count), // <--
,netLost,netReturned
//^--
]);
That creates a hole in the array. That's why you are seeing <1 empty item> in the output
console.log([1,,2])
const res = data.map((el) => [
el.lost,
...el.recovery_timespan.map((timespan) => timespan.count),
/* extra comma here --> */, el.netLost,
el.netReturned
])
[ [ 10, 1, 0, 10, 20 ], [ 7, 1, 3, 30, 200 ], [ 8, 1, 50, 40 ] ]
Not completly sure, but maybe try this.
...recovery_timespan.map(({count}) => count.count)

Hide or show two datasets with one click event of legend in chart.js

I want to show visualization of machines downtime for 2 shifts - day (12-hr) and night (12-hr) for 30 days. Thus, I use the stacked bar chart with groups and it looks good accept that I don't want to have the legends to show both shifts (day & night).
stacked bar chart with groups
<canvas id="myChart"></canvas>
<script>
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30",],
datasets: [{
label: 'Machine 1 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 1 - Night',
stack: 'Stack 1',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 2 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
},
{
label: 'Machine 2 - Night',
stack: 'Stack 1',
data: [12, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
},
]
},
options: {
plugins: {
legend: {
display: true
}
},
scales: {
xAxes: [{
stacked: true,
}],
yAxes: [{
stacked: true
}]
}
}
});
</script>
How do I combine the machine 1 or 2 for day & night shift in the legend and clicking machine 1 or 2 will hide both shifts (day & night)
stacked bar chart with groups edit Legend
I found the method below that might work for me.
https://www.chartjs.org/docs/3.1.0/configuration/legend.html#custom-on-click-actions
click handler of the first two datasets
How do I proceed for second two datasets of the click handler and so on accordingly.
<canvas id="myChart"></canvas>
<script>
var defaultLegendClickHandler = Chart.defaults.plugins.legend.onClick;
var newLegendClickHandler = function (e, legendItem, legend) {
var index = legendItem.datasetIndex;
if (index > 1) {
// Do the original logic
defaultLegendClickHandler(e, legendItem);
} else {
let ci = legend.chart;
[
ci.getDatasetMeta(0),
ci.getDatasetMeta(1)
].forEach(function (meta) {
meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
});
ci.update();
}
};
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30",],
datasets: [{
label: 'Machine 1 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 1 - Night',
stack: 'Stack 1',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 2 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
},
{
label: 'Machine 2 - Night',
stack: 'Stack 1',
data: [12, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
},
]
},
options: {
plugins: {
legend: {
onClick: newLegendClickHandler
}
},
scales: {
xAxes: [{
stacked: true,
}],
yAxes: [{
stacked: true
}]
}
}
});
</script>
First you need to define a legend.labels.generateLabels function that filter out undesired legend labels and changes the text of the remaining ones.
generateLabels: chart => chart.data.datasets.map((ds, i) => ({
text: ds.label.substring(0, ds.label.indexOf('-')),
datasetIndex: i,
fillStyle: chart.data.datasets[i].backgroundColor,
strokeStyle: chart.data.datasets[i].backgroundColor,
hidden: chart.getDatasetMeta(i).hidden
}))
.filter((ds, i) => i % 2),
Then you'll have to define a legend.onClick function that takes care of also showing/hiding the sibling dataset when the user clicks on a legend label.
onClick: (event, legendItem, legend) => {
let hidden = !legend.chart.getDatasetMeta(legendItem.datasetIndex).hidden;
(legendItem.datasetIndex == 1 ? [0, 1] : [2, 3])
.forEach(i => legend.chart.getDatasetMeta(i).hidden = hidden);
legend.chart.update();
}
For further details, see the Legend and API chapters of the Chart.js documentation.
Please take a look at your amended code and see how it works.
new Chart('chart', {
type: 'bar',
data: {
labels: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", ],
datasets: [{
label: 'Machine 1 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 1 - Night',
stack: 'Stack 1',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF4A4A',
},
{
label: 'Machine 2 - Day',
stack: 'Stack 0',
data: [5, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
},
{
label: 'Machine 2 - Night',
stack: 'Stack 1',
data: [12, 13, 5, 20, 4, 9, 28, 19, 21, 5, 13, 7, 21, 26, 10, 28, 19, 21, 30, 10, 27, 6, 12, 15, 4, 2, 13, 8, 29, 30],
backgroundColor: '#FF9C2A',
}
]
},
options: {
plugins: {
legend: {
labels: {
generateLabels: chart => chart.data.datasets.map((ds, i) => ({
text: ds.label.substring(0, ds.label.indexOf('-')),
datasetIndex: i,
fillStyle: chart.data.datasets[i].backgroundColor,
strokeStyle: chart.data.datasets[i].backgroundColor,
hidden: chart.getDatasetMeta(i).hidden
}))
.filter((ds, i) => i % 2),
},
onClick: (event, legendItem, legend) => {
let hidden = !legend.chart.getDatasetMeta(legendItem.datasetIndex).hidden;
(legendItem.datasetIndex == 1 ? [0, 1] : [2, 3])
.forEach(i => legend.chart.getDatasetMeta(i).hidden = hidden);
legend.chart.update();
}
}
},
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
}
}
});
canvas {
max-height: 190px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.js"></script>
<canvas id="chart"></canvas>
Hey I got something like this working, but I am hiding everything with the same stack variable and hiding certain data series as well so I can hide them without having their legend items available for click:
I have a lot of series that have the same datasets (ie, failed numbers, retested numbers and dont want to show those labels)
// Get Data From Views and Add Custom onClick Listener
let tempData = { ...views[i].data };
if (
// Kill the onClick and add a much slower but grouping onClick, hide all with part number in stack
tempData &&
tempData.options &&
tempData.options.plugins &&
tempData.options.plugins.legend
)
tempData.options.plugins.legend.onClick = (e, legendItem) => {
// My labels always start with the part number and a space so I grab that.
const partName = legendItem.text.split(' ')[0];
const chart = Chart.getChart('temp-chart');
// Toggle the data set that was clicked on
const datasetIndex = legendItem.datasetIndex;
if (!chart.getDatasetMeta(datasetIndex).hidden) chart.hide(datasetIndex);
else chart.show(datasetIndex);
let j = 0;
// Iterate through all datasets, cap at 200 for safety
while (chart.getDatasetMeta(j).stack && j < 200) {
const datasetMeta = chart.getDatasetMeta(j);
const stack = datasetMeta.stack;
const datasetName = datasetMeta.label;
// I add a stack id to all my datasets and use that to determine what is 'grouped'
// I have a plugin that does not render any datasets which do not have a label
// If the stack group matches and the dataset does not have a label it will be hidden.
if (
stack === partName &&
(datasetName == null ||
datasetName === '' ||
datasetName === 'undefined')
) {
if (!chart.getDatasetMeta(j).hidden) chart.hide(j);
else chart.show(j);
}
j++;
}
};
new Chart('temp-chart', { ...tempData });
And the legend plugin to hide legend items with no label.
Chart.defaults.plugins.legend.labels.filter = (item) => {
return item.text != null;
};
Heres what I got - Pictures

Javascript creates new arrays based on conditions

I have old and entity arrays:
var old = [
{
"id": 3,
"entity_type_id": 1,
"product_id": 4,
"name": "test1",
"acreage": 100,
"yield": 20,
"worth": 30
},
{
"id": 4,
"entity_type_id": 1,
"product_id": 4,
"name": "test2",
"acreage": 10,
"yield": 20,
"worth": 0
},
{
"id": 5,
"entity_type_id": 3,
"product_id": 5,
"name": "test3",
"acreage": 20,
"yield": 20,
"worth": 40
}
]
var entity = [
{"id": 1, "name": "a1"},
{"id": 2, "name": "a2"},
{"id": 3, "name": "a3"}
]
I hope to get the following data:
var newArr = [
{
"id": 3,
"entity_type_id": 1,
"product_id": 4,
"name": "test1",
"acreage": 110,
"yield": 40,
"worth": 30,
"entity_type_1": 2, // The total amount of entity_type_id (entity_type_id: 1)
"entity_type_2": 0,
"entity_type_3": 0
},
{
"id": 5,
"entity_type_id": 3,
"product_id": 5,
"name": "test3",
"acreage": 20,
"yield": 20,
"worth": 40,
"entity_type_1": 0,
"entity_type_2": 0,
"entity_type_3": 1 // The total amount of entity_type_id (entity_type_id: 3)
}
]
console.log(newArr)
I tried the following code and got some data. I'm not sure if there will be any exceptions or errors.
What's more, I don't know how to deal with the entity array data. Can someone help me solve this problem and get the result I expect?
Thank you very much !
function mergeArr(arr) {
const temp = []
arr.forEach((dataItem) => {
if (temp.length) {
let filterValue = temp.filter((items) => {
return items.product_id === dataItem.product_id
})
if (filterValue.length) {
temp.forEach((n) => {
if (n.product_id === filterValue[0].product_id) {
n.yield = dataItem.yield + filterValue[0].yield
n.acreage = dataItem.acreage + filterValue[0].acreage
n.worth = dataItem.worth + filterValue[0].worth
}
})
} else {
temp.push(dataItem)
}
} else {
temp.push(dataItem)
}
})
return temp
}
Youi could find the object and sum the wanted properties. For entity take another loop and map new entries and build a new object from it for spreading.
var old = [{ id: 3, entity_type_id: 1, product_id: 4, name: "test1", acreage: 100, yield: 20, worth: 30 }, { id: 4, entity_type_id: 1, product_id: 4, name: "test2", acreage: 10, yield: 20, worth: 0 }, { id: 5, entity_type_id: 3, product_id: 5, name: "test3", acreage: 20, yield: 20, worth: 40 }],
entity = [{ id: 1, name: "a1" }, { id: 2, name: "a2" }, { id: 3, name: "a3" }],
entityTypes = Object.fromEntries(entity.map(({ id }) => ['entity_type_' + id, 0])),
result = old.reduce((r, o) => {
let temp = r.find(q => q.product_id === o.product_id);
if (!temp) r.push(temp = { ... o, ...entityTypes });
else ['acreage', 'yield', 'worth'].forEach(k => temp[k] += o[k]);
temp['entity_type_' + o.entity_type_id]++;
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

JS Loop through array based on key values and add values

I have the following object inside an array:-
[
{"score": 5, "question": 0, "weight": 2},
{"score": 4, "question": 1, "weight": 2},
{"score": 3, "question": 0, "weight": 4},
{"score": 4, "question": 1, "weight": 4},
{"score": 2, "question": 2, "weight": 4},
{"score": 8, "question": 0, "weight": 2}
]
I am trying to loop through the array so I have the following output, so I am able to run some math against the results:-
[
[
{"score": 5, "question": 0, "weight": 2},
{"score": 4, "question": 1, "weight": 2}
],
[
{"score": 3, "question": 0, "weight": 4},
{"score": 4, "question": 1, "weight": 4},
{"score": 2, "question": 2, "weight": 4}
],
[
{"score": 8, "question": 0, "weight": 2}
]
];
Is there a dynamic way I am able to get array1 to look like array2?
I am using flat JS for this please no jQuery answers.
Thanks in advance.
** Note **
Sometimes each section will have more or less values, this is why I require it to be dynamic.
You can do this with reduce() method you just need to keep track of current index for final array.
const data =[
{score: 5, question: 0, weight: 2},
{score: 4, question: 1, weight: 2},
{score: 3, question: 0, weight: 4},
{score: 4, question: 1, weight: 4},
{score: 2, question: 2, weight: 4},
{score: 8, question: 0, weight: 2}
]
const result = data.reduce(function(r, e, i) {
if(i == 0) r = {values: [], counter: 0}
if(e.question == 0 && i != 0) r.counter++
if(!r.values[r.counter]) r.values[r.counter] = [e]
else r.values[r.counter].push(e)
return r;
}, {}).values
console.log(result)
You could check weight and if different, then take a new group.
var data = [{ score: 5, question: 0, weight: 2 }, { score: 4, question: 1, weight: 2 }, { score: 3, question: 0, weight: 4 }, { score: 4, question: 1, weight: 4 }, { score: 2, question: 2, weight: 4 }, { score: 8, question: 0, weight: 2 }],
grouped = data.reduce(function (r, o, i, a) {
if ((a[i - 1] || {}).weight !== o.weight) {
r.push([]);
}
r[r.length - 1].push(o);
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Array.reduce() to convert the 1st to the 2nd array. Whenever the current object weight doesn't match lastWeight, add another subarray. Always push the current item to the last subarray:
const arr = [
{score: 5, question: 0, weight: 2},
{score: 4, question: 1, weight: 2},
{score: 3, question: 0, weight: 4},
{score: 4, question: 1, weight: 4},
{score: 2, question: 2, weight: 4},
{score: 8, question: 0, weight: 2}
];
let lastWeight = null;
const result = arr.reduce((r, o) => {
if(lastWeight === null || o.weight !== lastWeight) {
lastWeight = o.weight;
r.push([]);
}
r[r.length - 1].push(o);
return r;
}, []);
console.log(result);
var oldA = [
{"score": 5, "question": 0, "weight": 2},
{"score": 4, "question": 1, "weight": 2},
{"score": 3, "question": 0, "weight": 4},
{"score": 4, "question": 1, "weight": 4},
{"score": 2, "question": 2, "weight": 4},
{"score": 8, "question": 0, "weight": 2}
];
var newA = [];
var prevW = 0;
var prevA;
for (var i = 0; i < oldA.length; i++) {
if (oldA[i].weight != prevW) {
prevA = [];
newA.push(prevA);
prevW = oldA[i].weight;
}
prevA.push(oldA[i]);
}
console.log(newA);
You can use array#reduce to group your array. Check if the value of question is 0 then push a new array and add the object to it.
var data = [ {"score": 5, "question": 0, "weight": 2}, {"score": 4, "question": 1, "weight": 2}, {"score": 3, "question": 0, "weight": 4}, {"score": 4, "question": 1, "weight": 4}, {"score": 2, "question": 2, "weight": 4}, {"score": 8, "question": 0, "weight": 2}],
result = data.reduce((r,o) => {
if(o.question == 0)
r.push([]);
r[r.length - 1].push(o);
return r;
},[]);
console.log(result);
let scores = [
{"score": 5, "question": 0, "weight": 2},
{"score": 4, "question": 1, "weight": 2},
{"score": 3, "question": 0, "weight": 4},
{"score": 4, "question": 1, "weight": 4},
{"score": 2, "question": 2, "weight": 4},
{"score": 8, "question": 0, "weight": 2}
]
let groupedScores = [], group = [];
scores.forEach((entry) => {
if(entry.question === 0) {
if (group.length) {
groupedScores.push(group);
}
group = [];
}
group.push(entry);
})
groupedScores.push(group)
console.log(groupedScores)
A bit simplified if splitting by just question: 0 :
data = [ { score: 5, question: 0, weight: 2 }, { score: 4, question: 1, weight: 2 },
{ score: 3, question: 0, weight: 4 }, { score: 4, question: 1, weight: 4 },
{ score: 2, question: 2, weight: 4 }, { score: 8, question: 0, weight: 2 } ]
result = data.reduce((r, v) => (v.question ? r[r.length-1].push(v) : r.push([v]), r), [])
console.log( result )

Categories

Resources