In my Vue app, I have a reduce function that's being called within an html/vue loop and it's taking far too long; around 21 seconds
During that time, nothing renders and the page freezes temporarily
I think part of the issue is that I'm calling the computed property in a loop and it calls the reduce function each time, but I'm still unclear on a way to optimize this to quickly go through the reduce function and allow the loop to only hit the result set as opposed to reducing through each iteration.
My result set is about 12,000 records but I've only included a few in the exact structure.
What can I do here?
<script>
const reduceFunction = (rows) =>
rows .reduce(
(a, row) => {
const employee = a [row .employee] || (a [row .employee] = {dates: {}, total_categories:0, total_items: 0, area: '', group: ''})
const date = employee .dates [row .itemDate] || (employee .dates [row .itemDate] = {categories: 0, qty: 0, total_categories: 0, unavailable: 0, orders: {}})
date.categories = row.categories_per_item * row.qty
date.qty = row.qty
date.total_categories = date.categories
const order = date .orders [row .order_number] || (date .orders [row .order_number] = {itemDate: '', skus: {}})
order.itemDate = row.itemDate;
const sku = order .skus [row .sku] || (order .skus [row .sku] = {categories: '', qty: '', itemDate: '', expected: '', created: '', unavailable: 0, available:0})
sku.categories = row.categories_per_item
sku.qty = row.qty
sku.itemDate = row.itemDate
sku.expected = row.shipDate
sku.created = row.created_date
sku.heir_id = row.heir_identifier
employee.total_categories = ( row.categories_per_item * row.qty)
employee.total_items = ( row.qty)
employee.area = row.area
employee.group = row.group_name
employee.warehouse = row.warehouse
employee.locale = row.locale
const foundKit = vm.$data.kitsData.find((kit) => kit.heir_identifier === sku.heir_id)
if (foundKit) {
new_avail = 10;
if(sku.qty > new_avail){
status.status = "Not available";
date.unavailable = 1
sku.unavailable = 1
}else{
status.status = "Available"
}
}else{
status.status = "No item found"
}
return a
},
{}
);
var vm =
new Vue({
el: "#app",
data: {
rows: [
{
employee: "Adam",
sku: "A1453",
categories_per_item: "15",
area: "1",
itemDate: "2021-11-02",
qty: 37,
group_name: "managers",
warehouse: "3",
order_number: "1234",
locale: "1",
shipDate: "2020-02-02",
created_date: "2020-01-01",
heir_identifier:"ABC3"
},
{
employee: "Joan",
sku: "A1453",
categories_per_item: "15",
area: "1a",
itemDate: "2021-11-02",
qty: 17,
group_name: "managers",
warehouse: "3",
order_number: "34578",
locale: "1",
shipDate: "2020-02-02",
created_date: "2020-01-01",
heir_identifier:"ABC3"
},
{
employee: "Bill",
sku: "A1453",
categories_per_item: "15",
area: "1",
itemDate: "2021-11-03",
qty: 57,
group_name: "managers",
warehouse: "3",
order_number: "2345",
locale: "1",
shipDate: "2020-02-02",
created_date: "2020-01-01",
heir_identifier:"ABC3"
},
{
employee: "PJ",
sku: "A6512",
categories_per_item: "150",
area: "2",
itemDate: "2021-11-03",
qty: 20,
group_name: "managers",
warehouse: "3",
order_number: "34567",
locale: "1",
shipDate: "2020-02-02",
created_date: "2020-01-01",
heir_identifier:"ABC1"
}
]
},
methods: {
},
computed: {
employeeData() {
console.log('employee data')
employeeRows = reduceFunction(this.rows)
return employeeRows
console.log(employeeRows)
},
dates() {
return Array.from(Array(11), (_, i) => new Date(Date.now() i * 86400000).toISOString().slice(0,10))
}
}
});
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<tr v-for="(value, employee) in employeeData" :key="employee">
<td>@{{employee}}</td>
<td v-for="date in dates" :key="date" >
<div v-for="(dateInfo, dateValue) in value.dates" :key="dateValue" >
<div v-if="dateValue == date ">
@{{ dateInfo.total_categories }}
</div>
</div>
</td>
</tr>
</div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
The main improvement I could see would be to eliminate the nested loop here:
const foundKit = vm.$data.kitsData.find((kit) => kit.heir_identifier === sku.heir_id)
Organize the kitsDat
by heir_identifier
first so you can look up in O(1)
instead of .find
ing (O(n)
) each time.
const kitsByHeir = new Map();
for (const kit of vm.$data.kitsData) {
kitsByHeir.set(kit.heir_identifier, kit);
}
Then do kitsByHeir.get(sku.heir_id)
inside the loop.
You might also use a for
loop instead of reduce
(reduce
is arguably not appropriate in this situation anyway)
Also, processing 12,000 records on the client-side is pretty odd. Even with the best designed code, that could take an uncomfortable amount of time in certain environments. Consider moving the processing to a server instead.
CodePudding user response:
My approach for this problem would be to invoke reduceFunction
on mounted(){}
and create another state for the array, here I called it parsedRow
So basically to avoid unnecessary re rendering.
data: {
rows: []
parsedRows: []
}
methods: {
reduceFunction(data){
//adjust your code to fit method here
}
}
mounted(){
this.parsedRows = this.reduceFunction(this.rows);
}
and then use the parsedRows
on the Vue template.
Also to move the reduceFunction to methods