Reduce function works but takes too long, vue - javascript

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>

My approach for this problem would be to invoke reduceFunction on mounted(){}
and create another state for the array, here I called it parsedRows
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

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 .finding (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.

Related

Reduce function wrapping data in an array

I have a reduce function that formats my data in the way i need but the only issue is the data is nested inside an array. I need to remove the outter array or just stop the reduce function from adding it in but every attempt Ive made to stop the reducer from wrapping the data in an array breaks my code. Ideally I would like my reducer to not wrap the data in an array but if thats not possible removing the array i need from inside the reducer cleanly seems like the only solution:
my data looks like this:
{
count: 4,
datapoints: [
{
Date: "2021-05-05",
score: 0,
},
{
Date: "2021-05-12",
score: 0,
},
{
Date: "2021-05-30",
score: 0,
},
{
Date: "2021-06-03",
score: 114,
},
],
};
my reducer function and api call:
const riskScores = await api.PCC.riskAssessment(userID, startdate, endDate);
const riskScoresFormatted = riskScores.datapoints.reduce((result, data) => {
const scores = result["riskAssessment"] || [];
scores.push({
value: data.score,
unit: "none",
recordedDate: data.Date,
method: "none",
type: "riskAssessment",
});
result["riskAssessment"] = scores;
return result;
}, []);
the output:
[riskAssessment: [{…}, {…}, {…}, {…}] ]
Ive tried just using the index of riskScoresFormatted[0] that comes back undefined. riskScoresFormatted.slice(1) just returns an empty array. Ive also tried targeting the first Item like riskScoresFormatted.riskAssessment this works but the value is sometimes null so it causes bugs later down the line.
Try changing the final reduce argument from [] to {} and I think you'll have better luck.
const riskScoresFormatted = riskScores.datapoints.reduce((result, data) => {
const scores = result["riskAssessment"] || [];
scores.push({
value: data.score,
unit: "none",
recordedDate: data.Date,
method: "none",
type: "riskAssessment",
});
result["riskAssessment"] = scores;
return result;
}, {});
Or, use Array.map() instead:
const riskScores = {
count: 4,
datapoints: [{
Date: "2021-05-05",
score: 0,
},
{
Date: "2021-05-12",
score: 0,
},
{
Date: "2021-05-30",
score: 0,
},
{
Date: "2021-06-03",
score: 114,
},
],
};
var riskScoresFormatted = riskScores.datapoints.map((data) => ({
value: data.score,
unit: "none",
recordedDate: data.Date,
method: "none",
type: "riskAssessment",
}));
console.log(riskScoresFormatted);

Looping through nested data and displaying object properties and values

In my React app, I'm looking for a clean way to loop through the following dynamic data structure and display the object properties and values.
Sample data:
data: {
company: [
{
company_name: "XYZ Firm",
company_email: "hello#xyz.com",
company_phone: 91982712,
}
],
shareholders: [
{
shareholder_name: "Lin",
percentage: 45
},
{
shareholder_name: "Alex",
percentage: 10
},
],
employees: [
{
employee_name: "May",
employee_email: "may#xyz.com"
},
]
}
The output I want is:
company_name: XYZ Firm
company_email: hello#xyz.com
company_phone: 91982712
shareholder_name: Lin
shareholder_percentage: 45
shareholder_name: Alex
shareholder_percentage: 10
employee_name: May
employee_email: may#xyz.com
This is what I've tried so far:
//data contains the entire object
const profileInfo = Object.keys(data).map(key => {
let profileSection = [];
for (let values of data[key]) { //retrieve the objects of each "section" e.g., company, shareholders
Object.keys(values).map(key => {
profileSection.push(<p>{key}: {values[key]}</p>);
})
}
return profileSection;
})
I'm able to achieve the intended results but I'm not sure if it's the best solution in terms of performance. Having nested Object.keys().mapseems a bit off to me.
Note: User will be able to add more shareholders/employees.
Here is a somewhat shorter version using Object.values() and Object.entries().
var data = { company: [ { company_name: "XYZ Firm", company_email: "hello#xyz.com", company_phone: 91982712, } ], shareholders: [ { shareholder_name: "Lin", percentage: 45 }, { shareholder_name: "Alex", percentage: 10 }, ], employees: [ { employee_name: "May", employee_email: "may#xyz.com" }, ] };
let profileInfo = [];
Object.values(data).flat().forEach((item) => {
Object.entries(item).forEach(([key, value]) => {
profileInfo.push(key + ": " + value);
});
});
console.log(profileInfo);

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)

create pagination for search filter along with sorting table using knockout js

I am creating table with pagination and search filter. We bind table data with search filter, Is the possible to crate search filter in table with pagination. How to create pagination in table, and also perform search filter, along with sorting also.
function PlayersViewModel() {
var self = this,x, i, suits;
/* text field should be empty */
self.filterText = ko.observable(""); // Text from search field
//#region Constant Properties
self.descending = "fa fa-arrow-down";
self.ascending = "fa fa-arrow-up";
//#endregion
self.CurrentPage = ko.observable(1); // Store the current page of the user
self.DataPerPage = ko.observable(5); // To identify how many data we want to see per page
//#region Knockout Observables
// Observable array that represents each column in the table
self.columns = ko.observableArray([
{ property: "id", header: "ID", type: "number", state: ko.observable("") },
{ property: "name", header: "Name", type: "string", state: ko.observable("") },
{ property: "time", header: "Time", type: "date", state: ko.observable("") },
{ property: "dob", header: "Date of Birth", type: "date", state: ko.observable("") },
{ property: "address", header: "Address", type: "string", state: ko.observable("") },
{ property: "msg", header: "Body", type: "string", state: ko.observable("") }
]);
// Observable array that will be our data
self.players = ko.observableArray([
{ id: "01", name: "Micky", time: "2015-02-09 4:46:56.678", dob: "10/20/1974", address: "London", msg: "Received Message" },
{ id: "02", name: "Griffey", time: "2015-03-09 4:46:54.678", dob: "11/21/1969", address: "Russia", msg: "Received Message" },
{ id: "03", name: "Derek", time: "2015-01-07 4:46:55.678", dob: "6/26/1931", address: "London", msg: "Send Message" },
{ id: "04", name: "Steven", time: "2015-03-09 4:46:56.678", dob: "2/10/1963", address: "Russia", msg: "Send Message" },
{ id: "05", name: "Jafer", time: "2015-02-09 5:46:56.540", dob: "12/18/1886", address: "London", msg: "Received Message" },
{ id: "06", name: "Jenifer", time: "2015-01-14 4:52:16.379", dob: "11/29/1970", address: "China", msg: "Send Message" },
{ id: "07", name: "Jones", time: "2015-03-08 5:26:33.600", dob: "8/24/1895", address: "London", msg: "Received Message" },
{ id: "08", name: "Micky", time: "2015-02-09 4:46:56.678", dob: "10/20/1974", address: "London", msg: "Received Message" },
{ id: "09", name: "Bruce", time: "2015-03-09 4:46:54.678", dob: "11/21/1969", address: "Russia", msg: "Received Message" },
{ id: "10", name: "Peter", time: "2015-01-07 4:46:55.678", dob: "6/26/1931", address: "London", msg: "Send Message" },
{ id: "11", name: "Wayne", time: "2015-03-09 4:46:56.678", dob: "2/10/1963", address: "Russia", msg: "Send Message" },
{ id: "12", name: "Sam", time: "2015-02-09 5:46:56.540", dob: "12/18/1886", address: "London", msg: "Received Message" },
{ id: "13", name: "Jenifer", time: "2015-01-14 4:52:16.379", dob: "11/29/1970", address: "China", msg: "Send Message" },
{ id: "14", name: "Smith", time: "2015-03-08 5:26:33.600", dob: "8/24/1895", address: "London", msg: "Received Message" }
]);
//#endregion
/* script for search filter */
suits = self.players();
for ( i = 0; i < suits.length; i++) {
suits[i]["search_content"] = ">";
for ( x in suits[i] ) {
if ( !suits[i].hasOwnProperty(x) || x == "search_content" || typeof suits[i][x] !== "string") {continue;}
suits[i]["search_content"] += suits[i][x].toUpperCase();
}
}
// Collection of players after going through search filter
self.filteredTestsuites = ko.computed(function () {
var reg;
// If many white spaces in a row, replace with only one white space
fText = self.filterText().replace(/\s+/gi, '|');
fText = fText.replace(/\|\s*$/gi, '');
console.log("regex:", fText);
reg = new RegExp(fText, "gi");
// If there is anything in the search box, filter for this
// As of now this does not divide the filterText and only searches the Name field
var filteredCollection = ko.utils.arrayFilter(self.players(), function(test) {
if(fText.length)
return test.search_content.match(reg);
else
return 1;
});
return filteredCollection;
}, self);
/* script Ends with text search */
/* script for sorting */
//#region Sorting methods
self.sortClick = function (column) {
try {
// Call this method to clear the state of any columns OTHER than the target
// so we can keep track of the ascending/descending
self.clearColumnStates(column);
// Get the state of the sort type
if (column.state() === "" || column.state() === self.descending) {
column.state(self.ascending);
}
else {
column.state(self.descending);
}
console.log(column.state());
console.log(column.type);
switch (column.type) {
case "number":
self.numberSort(column);
break;
case "date":
self.dateSort(column);
break;
case "string":
default:
self.stringSort(column);
break;
}
}
catch (err) {
// Always remember to handle those errors that could occur during a user interaction
alert(err);
}
};
// Generic sort method for numbers and strings
self.stringSort = function (column) { // Pass in the column object
self.players(self.players().sort(function (a, b) {
// Set strings to lowercase to sort properly
var playerA = a[column.property].toLowerCase(), playerB = b[column.property].toLowerCase();
if (playerA < playerB) {
return (column.state() === self.ascending) ? -1 : 1;
}
else if (playerA > playerB) {
return (column.state() === self.ascending) ? 1 : -1;
}
else {
return 0
}
}));
};
self.numberSort = function (column) {
self.players(self.players().sort(function (a, b) {
var playerA = a[column.property], playerB = b[column.property];
if (column.state() === self.ascending) {
return playerA - playerB;
}
else {
return playerB - playerA;
}
}));
};
// Sort by date
self.dateSort = function (column) {
self.players(self.players().sort(function (a, b) {
if (column.state() === self.ascending) {
return new Date(a[column.property]) - new Date(b[column.property]);
}
else {
return new Date(b[column.property]) - new Date(a[column.property]);
}
}));
};
//#region Utility Methods
self.clearColumnStates = function (selectedColumn) {
var otherColumns = self.columns().filter(function (col) {
return col != selectedColumn;
});
for (var i = 0; i < otherColumns.length; i++) {
otherColumns[i].state("");
}
};
/* script Ends for sorting */
};
ko.applyBindings(new PlayersViewModel());
They should perform search filter and sorting in the table correctly, how to create pagination for the sample, they do not affect the search filter and sorting in the table.
The basics of pagination using a ko.pureComputed:
const paginatedData = ko.pureComputed(
() => dataSource().slice(
currentPage() * pageSize(),
currentPage() * pageSize() + pageSize()
)
);
In your specific case, dataSource is filteredTestsuites, currentPage is CurrentPage and pageSize is DataPerPage.
Things to keep in mind:
- Fix the lower limit of currentPage to 0
- Fix the upper limit of currentPage to Math.floor(dataSource().length)
- Make sure that when the dataSource changes in length (i.e. when filtering), the currentPage is updated to match the new restrictions
- Make sure that when pageSize is made larger, the currentPage is updated to match the new restrictions.
const currentPage = ko.observable(0);
const pageSize = ko.observable(5);
const dataSource = ko.observableArray(
Array.from({ length: 100 }, (_, i) => i)
);
const paginatedData = ko.pureComputed(
() => dataSource().slice(
currentPage() * pageSize(),
currentPage() * pageSize() + pageSize()
)
);
ko.applyBindings({
next: () => currentPage(currentPage() + 1),
prev: () => currentPage(currentPage() - 1),
currentPage,
paginatedData
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: paginatedData">
<li data-bind="text: $data"></li>
</ul>
<button data-bind="click: prev"><</button>
<span data-bind="text: currentPage"></span>
<button data-bind="click: next">></button>

Angular POS app, trying to generate an automatic purchase order from a POJO

I'm x-referencing a few things inside of a products object, namely, stock levels, low stock levels, suppliers, min-order qty, and cost. The method should create an order object for each supplier with each product whose stock is lower than low-stock levels.
Using this object format:
0: {
$$hashKey: "081",
$id: "-JPuOUmkvwpXB-99QuU6",
brand: "",
categories: Array[2],
events: Array[1],
imgUrl: "http://crayons-roleplay.weebly.com/uploads/2/4/9/6/24964708/735664_orig.jpg",
lowStock: "10",
messages: Array[1],
minOrder: "25",
name: "Crazy Crayons - Eco stars (individual)",
notes: Array[1],
price: "0.5",
quantity: "",
size: "",
sku: "912143",
stock: "21",
suppliers: [
0: {,
caseSize: ""
name: "Crazy Crayons"
price: 0.29
sku: ""
units: ""
tax: "15"
units: ""
upc: "91214"
updated: 1404907608273
}
]
1: {
$$hashKey: "082"
$id: "-JPuOUmnj9r6wGx27qVE"
brand: ""
categories: Array[1]
events: Array[1]
lowStock: 0
messages: Array[1]
minOrder: 4
name: "Lifefactory 12 oz Straw Cap - Coral"
notes: Array[1]
price: "26"
quantity: ""
size: ""
sku: "45635971011"
stock: "0"
suppliers: Array[1]
0: Object
caseSize: ""
name: "SOKO"
price: 13
sku: ""
units: ""
tax: ""
units: ""
upc: "45635971011"
updated: "2014-07-07T17:02:49.089Z"
}
action:function(){
Making a list of products with low stock:
angular.forEach(data.products.array, function(value){
if(value.stock < value.lowStock){
if(angular.isObject(value)){
$scope.generatedOrder.push({id:value.$id,upc:value.upc,'min-order':value.minOrder,supplier:value.suppliers});
}
}
});
$scope.supplierList = [];
$scope.supplierResults = [];
Pulling a list of suppliers from low-stock products:
angular.forEach($scope.generatedOrder, function(order){
angular.forEach(order.supplier, function(supplier){
if ($scope.supplierList.indexOf(supplier.name) == -1) {
$scope.supplierList.push(supplier.name);
}
});
});
angular.forEach($scope.supplierList, function(supplier){
$scope.supplierResults[supplier]=[];
});
Troublesome facet, I've tried many different ways to create an order key'd by the supplier. The closest I've come to victory is an array of supplier objects, with the right key but no value.
for(var i=0;i<Object.keys($scope.generatedOrder).length ;i++){
for(var j=0;j<$scope.supplierList.length;j++){
if (!angular.isArray(supplierResults[$scope.supplierList[j]])){
supplierResults[$scope.supplierList[j]] = [];
}
if ($scope.generatedOrder[i].supplier !== undefined){
if ( $scope.generatedOrder[i].supplier['0'].name === $scope.supplierList[j]) {
supplierResults[$scope.supplierList[j]].unshift($scope.generatedOrder[i]);
$scope.supplierResults = supplierResults;
}
}
}
}
Now, this almost works. The proper objects are built up and show in the array but the array reads length: 0 so I tried to have the length of $scope.supplierResults = supplierResults in here to make the browser think the array is full and normal.
$scope.supplierResults.length = supplierResults.length;
console.log($scope.supplierResults);
return $scope.supplierResults;
The end result should be like $scope.supplierResults = [{supplier:'xyz', items:[{id:'4g245t2g424g45t',name:apple, sku:453524, cost:3, qty:10}]}, {supplier:'abc',items:[{id:'3982462398uih23j',orange, sku:32545, cost:2, qty:12}]}]
EDIT: Here is updated, correct code. I accidentally left the target out of the "omit" and forgot to call value() to end my chain in the inner function right after the omit.
var result = _(data)
.filter(function(product){
return product.stock < product.lowStock && _.isObject(product);
})
.map(function(product){
return _(product.suppliers).map(function(supplier){
return {supplier:supplier, product : _.omit(product, 'suppliers')};
}).value();
})
.flatten()
.groupBy(function(productAndSupplier){
return productAndSupplier.supplier.name;
})
.map(function(productAndSupplierGroup){
return _(productAndSupplierGroup).reduce(function(memo, current){
memo.supplier = current.supplier;
memo.items.push(current.product);
return memo;
},{items:[]})
})
.value();
And a plunkr to show it in action: http://plnkr.co/edit/XAiyWauCdKmHTS3unqO5 (note that you will have to look in the browser console to see the output)

Categories

Resources