Multi Level JSON Filter - arrays

So I have this json:
this.state = {
projects: [{
id: 10,
name: "Project 10",
runTimes: [{
id: 186,
name: "Do Homeworks",
start: "2020-W01",
end: "2020-W09",
project: 10,
users: [{
id: 1,
name: "Sander Cokart",
runTime: [
186
]
}]
}],
hidden: false
}, ]
}
And I wish to filter based on runTimes.start & end,
I have already tried this:
const filtered = array.filter((project) => {
if (!project.hidden) {
if (project.runTimes.filter((runTime) => {
if (moment(runTime.start).isSameOrAfter(context.searchFrom) &&
moment(runTime.end).isSameOrBefore(context.searchTo)) {
return runTime;
}
}).length > 0) {
return project;
}
}
});
sadly it doesn't work the second a second RunTime exists inside a project object RunTimes array.
anyone have an idea?

The filter functions are just a bit off. I'd argue the if statement wrapped around the filter is confusing, I'd go for something simpler. This is working for me:
const result = array.projects.filter((project) => {
return !project.hidden && project.runTimes.filter(runTime => {
return moment(runTime.start).isSameOrAfter("2020-W01") &&
moment(runTime.end).isSameOrBefore("2020-W12")
})
});

Related

Handling relational data in Zustand

I need some input from people more experienced with Zustand to share their way of managing relational state. Currently we have the following:
Let's assume we have the example entities Campaign, Elementss and their Settings. The REST API returning them is in the following format:
GET <API>/campaigns/1?incl=elements,elements.settings
{
"id":1,
"name":"Welcome Campaign",
"elements":[
{
"id":5,
"type":"heading",
"label":"Heading",
"content":"Welcome!",
"settings":[
{
"id":14,
"name":"backgroundColor",
"value":"#ffffff00"
},
{
"id":15,
"name":"color",
"value":"#ffffff00"
}
]
},
{
"id":6,
"type":"button",
"label":"Button",
"content":"Submit",
"settings":[
{
"id":16,
"name":"backgroundColor",
"value":"#ffffff00"
},
{
"id":17,
"name":"color",
"value":"#ffffff00"
},
{
"id":18,
"name":"borderRadius",
"value":"3px"
}
...
]
}
...
]
}
What we are currently doing in the Reactjs app is fetching this data, then transforming it to the following normalized format and set functions:
const useCurrentCampaignStore = create(
combine(
{
campaign: {
id: 1,
name: "Welcome Campaign"
},
elements: [
{
id: 5,
campaignId: 1,
type: "heading",
label: "Heading",
content: "Welcome!"
},
{
id: 6,
campaignId: 1,
type: "button",
label: "Button",
content: "Submit"
}
],
settings: [
{
id: 14,
elementId: 5,
name: "backgroundColor",
value: "#ffffff00"
},
{
id: 15,
elementId: 5,
name: "color",
value: "#ffffff00"
},
{
id: 16,
elementId: 6,
name: "backgroundColor",
value: "#ffffff00"
},
{
id: 17,
elementId: 6,
name: "disabled",
value: false
},
{
id: 18,
elementId: 6,
name: "borderRadius",
value: 3,
}
]
},
set => ({
updateSetting: (id: string | number, newValue: string | number | boolean) =>
set(state => {
const settings = [...state.settings];
return {
...state,
settings: settings.map(setting => {
if (setting.id == id) {
return { ...setting, value: newValue };
}
return setting;
})
};
}),
updateElementContent: (id: string | number, newValue: string) => {
set(state => {
const elements = [...state.elements];
return {
...state,
elements: elements.map(element => {
if (element.id == id) {
return { ...element, content: newValue };
}
return element;
})
};
});
}
})
)
);
I am, however, not sure this is the optimal solution, because It's rather tedious transforming all GET requests to a normalized format and then converting them back to nested objects when you want to construct either a POST, PUT or PATCH request.
So, to put it short, how do you guys design the state in your Zustand-based RESTful-API-backed React apps?

How can I change TS object array format?

Array which I want change
I have got this array in TypeScript, I want it change as bellow.
list = [{id : 1, name : bus, active : 1}, {id : 2, name : car}]
Can anyone help me?
You can map the input and reduce each element to an object.
const list = [
[{ id: 1 }, { name: 'bus' }, { active: 1 }],
[{ id: 2 }, { name: 'car' }],
];
const result = list.map((el) => {
return el.reduce((acc, curr) => {
return { ...acc, ...curr };
}, {});
});
console.log(result);

unique objects by value and extra filter within an array of objects

I am trying to get unique objects within an array of objects. Let's say the object is like below.
values = [
{ id: 10, available: true },
{ id: 10, available: false },
{ id: 11, available: true },
{ id: 12, available: false }
];
unique objects should return like below: if duplicates objects contains available:true need available:true
result = [
{ id: 10, available: true },
{ id: 11, available: true },
{ id: 12, available: false }
];
A solution to your problem..
The function myfilter() returns a array with unique id values, and if any exist with available: true it takes precedence
var values = [
{ id: 10, available: true },
{ id: 10, available: false },
{ id: 11, available: true },
{ id: 12, available: false }
];
function myfilter()
{
var filtarray=[];
values.forEach(function(element) {
var idxfound = filtarray.findIndex(function(ele2) {
return ele2.id === element.id;
});
if (idxfound==-1)
{
filtarray.push(element);
}
else {
if (filtarray[idxfound].available==false && element.available==true)
filtarray[idxfound].available=true;
}
});
console.log(filtarray);
return filtarray;
}

PrimeNG p-table Lazy loading and Multi Sort - Goes in Loop - Maximum call stack size exceeded

Stackblitz: https://stackblitz.com/edit/primeng-p-table-multisort
Config:
"#angular/cli": "~7.0.2",
"primeng": "7.0.5",
I have a PrimeNG p-table implemented with lazy loading. Need multi column sort added to it.
Sample code from above Stackblitz link.
<p-table [columns]="cols" [value]="cars1" [lazy]="true" [lazyLoadOnInit]="false" (onLazyLoad)="loadList($event)" sortMode="multiple" [multiSortMeta]="multiSortMeta">
This works properly if its single sort mode.
Getting error as ERROR RangeError: Maximum call stack size exceeded.
It should be a simple implementation but not able to understand what is missing here or this is not supported by PrimeNG.
Any help is appreciated.
This issue was because of this.cdRef.detectChanges(); Here is why
loadList is binded to (onLazyLoad)="loadList($event)" in HTML.
PrimeNg calls that event every time when paging, sorting, and filtering happens. So, when we are loading and adding sorting events it keeps calling. And Angular change detection also called every time, that leads to error ERROR RangeError: Maximum call stack size exceeded
loadList($event: any = {}) {
this.cars1 = [
{
vin: "a1653d4d",
brand: "VW",
year: 1998,
color: "White",
price: 10000
},
{
vin: "ddeb9b10",
brand: "Mercedes",
year: 1985,
color: "Green",
price: 25000
}
];
this.cdRef.detectChanges(); // this is the issue
}
modified
ngOnInit() {
this.cols = [
{ field: "vin", header: "Vin" },
{ field: "year", header: "Year" },
{ field: "brand", header: "Brand" },
{ field: "color", header: "Color" }
];
this.multiSortMeta = [
{ field: "year", order: 1 },
{ field: "brand", order: -1 }
];
this.loadList();
}
loadList($event: any = {}) {
this.cars1 = [
{
vin: "a1653d4d",
brand: "VW",
year: 1998,
color: "White",
price: 10000
},
{
vin: "ddeb9b10",
brand: "Mercedes",
year: 1985,
color: "Green",
price: 25000
}
];
// this.cdRef.detectChanges();
}
The bottom line is this.cdRef.detectChanges(); need to be used carefully and smartly in anywhere in the application if you are using it.
updated stackblitz
Updated Stackblitz: https://stackblitz.com/edit/primeng-p-table-multisort
It worked with overriding the PrimeNG Table - sortMultiple method via prototype chain.
Old code:
Table.prototype.sortMultiple = function () {
var _this = this;
if (this.multiSortMeta) {
if (this.lazy) {
this.onLazyLoad.emit(this.createLazyLoadMetadata());
}
else if (this.value) {
if (this.customSort) {
this.sortFunction.emit({
data: this.value,
mode: this.sortMode,
multiSortMeta: this.multiSortMeta
});
}
else {
this.value.sort(function (data1, data2) {
return _this.multisortField(data1, data2, _this.multiSortMeta, 0);
});
}
if (this.hasFilter()) {
this._filter();
}
}
this.onSort.emit({
multisortmeta: this.multiSortMeta
});
this.tableService.onSort(this.multiSortMeta);
}
};
New code:
Table.prototype.sortMultiple = function () {
const _this = this;
if (this.multiSortMeta) {
if (this.lazy) {
// additional conditions added
if (this.lazyLoadOnInit || (!this.lazyLoadOnInit && this.initialized)) {
this.onLazyLoad.emit(this.createLazyLoadMetadata());
}
} else if (this.value) {
if (this.customSort) {
this.sortFunction.emit({
data: this.value,
mode: this.sortMode,
multiSortMeta: this.multiSortMeta
});
} else {
this.value.sort(function (data1, data2) {
return _this.multisortField(data1, data2, _this.multiSortMeta, 0);
});
}
if (this.hasFilter()) {
this._filter();
}
}
this.onSort.emit({
multisortmeta: this.multiSortMeta
});
this.tableService.onSort(this.multiSortMeta);
}
};

ant design table with dynamic children columns

I'm using ant's table and trying to setup dynamic columns.
What I need is a table that shows a list of users with their performance for each of the classes as in the example below.
Details: I have a grouped column Performance that can have different sub-columns (current example shows columns science and physics). I'm calling renderContent() which sets up an object that has property children. I found this "solution" from ant's example here. The problem is that ant's example outputs children prop type string, while my function outputs prop type array. Which results in the error.
Here is a link to sandbox:
https://codesandbox.io/embed/ecstatic-cookies-4nvci?fontsize=14
Note: if you uncomment children array in columns [line 46-59], you will see what my expected result should be.
The render method shouldn't return the object with children array. To use the render method, you would have to return a valid React component (or simply HTML tag ---like span).
However in your case, I prefer we extract subjects before passing it into the table and then generate children array dynamically. Something like below:
const renderContent = (value, row, index) => {
return setupPerformance(value)
};
const setupPerformance = performance => {
return performance.map(p => {
const { id, title, percentage } = p;
return <span>{percentage}%</span>
});
};
const data = [
{
key: 0,
firstName: "John",
lastName: "Smith",
performance: [
{
id: 1,
title: "science",
percentage: 75
},
{
id: 2,
title: "physics",
percentage: 36
}
]
},
{
key: 1,
firstName: "Ann",
lastName: "Smith",
performance: [
{
id: 1,
title: "science",
percentage: 68,
timeSpent: 50,
completionDate: "2019-02-07"
},
{
id: 2,
title: "physics",
percentage: 100
}
]
}
];
let subjects = data[0].performance
const columns = [
{
title: "Full Name",
children: [
{
title: "firstName",
dataIndex: "firstName",
key: "firstName"
},
{
title: "lastName",
dataIndex: "lastName",
key: "lastName"
}
]
},
{
title: "Performance",
dataIndex: "performance",
children:
subjects.map(e => {
return {
title: e.title,
dataIndex: "performance["+(e.id-1)+"].percentage",
key: "key-"+e.id,
render: value => <span>{value}%</span>
}
})
}
];
Because of the solution in answer from Mobeen does not work anymore, I have tried to solve this.
I have extended the render method for the children columns of performance column:
...
{
title: "Performance",
dataIndex: "performance",
children: subjects.map((assessment) => {
const { title, id } = assessment;
return {
title,
dataIndex: "performance",
key: id,
render: (values) =>
values.map((value, index) => {
let ret;
if (index === id - 1) ret = values[index].percentage + "%";
return ret;
})
};
})
}
...
It returns only the percentage value of the subject with the corresponding id.
It is not very clean, but it works.
Check the solution in sandbox: https://codesandbox.io/s/prod-lake-7n6zgj

Resources