Is there a way that I can simplify this code?
I was thinking if there is a way to set { ...filterItem, type: 'chip' } as the parameter in map function instead of creating a const that will be returned in each state.
Is this type of syntax possible to do? If so, is there a specific term for it?
filtersToUse = filtersToChip.map((filterItem) => {
const filterItem2 = { ...filterItem, type: 'chip' }
if (filterItem.id === '12345') {
return { ...filterItem2, labelOverride: 'new-label' }
} else if (filterItem.id === '67890') {
return { ...filterItem2, labelOverride: 'new-label' }
}
return filterItem2
})
Seems like you want to:
Add type: 'chip' too all the elements
Add labelOverride: 'new-label' if id is 12345 OR 67890
You could do something like:
filtersToUse = filtersToChip.map((filterItem) => ({
...filterItem,
type: 'chip',
...([ '12345', '67890'].includes(filterItem.id) ? { labelOverride: 'new-label' } : {})
});
Where we use object spreading to add the desired options, if needed
Couldn't you do this:
filtersToUse = filtersToChip.map((filterItem) => ({
...filterItem,
type: 'chip',
labelOverride: ['12345', '67890'].includes(filterItem.id)
? 'new-label'
: undefined,
}));
I don't know if that is what you're searching for but i would optimize like that.
const newLabelItemIds = ['12345', '67890'];
const filtersToUse = filtersToChip.map((filterItem) => {
const label = newLabelItemIds.include(filterItem.id) ? { label: 'new-label' } : {};
return {
...filterItem,
...label,
type: 'chip',
};
});
Related
I want the difference in such a way that the I don't return the entire nested object if any of the values is different.
I have seen solutions online and they all return the entire nested objects and it doesn't work if only 1 key-value pair is changed. i don't want to show the difference as a complete nested object. it should be easier for any user to read.
for eg:
const A = {
position: 2,
attributes: [{
code: 123,
name: "xyz",
params: {
label: "hehe",
units: "currency"
}
}],
code: 1
}
const B = {
position: 3,
attributes: [{
code: 123,
name: "xyzr",
params: {
label: "heh",
units: "currency"
}
}],
code: 1
}
I want the output to be like this:
difference: {
position: {
current: 2,
previous: 3
},
attributes: {
current : [{ name: "xyz", params: { label: "hehe" } }],
previous: [{ name: "xyzr", params: {label: "heh"}}]
}
}
The code that I tried:
const compareEditedChanges = (A: any, B: any) => {
const allKeys = _.union(_.keys(A), _.keys(B));
try {
setDifference(
_.reduce(
allKeys,
(result: any, key) => {
if (!_.isEqual(A?.[key], B?.[key])) {
result[key] = {
current: A[key],
previous: B[key]
};
}
return result;
},
{}
)
);
} catch (err) {
console.error(err);
}
return difference;
};
After giving it a lot of thought to the code, I came with my own solution for a deeply nested objects comparison and listing out the differences in an object with keys as current and previous.
I didn't use any inbuilt libraries and wrote the code with simple for loop, recursion and map
const compareEditedChanges = (
previousState,
currentState
) => {
const result = [];
for (const key in currentState) {
// if value is string or number or boolean
if (
typeof currentState[key] === 'string' ||
typeof currentState[key] === 'number' ||
typeof currentState[key] === 'boolean'
) {
if (String(currentState[key]) !== String(previousState[key])) {
result.push({
[key]: {
current: currentState[key],
previous: previousState[key]
}
});
}
}
// if an array
if (
Array.isArray(currentState[key]) ||
Array.isArray(previousState[key])
) {
console.log(currentState[key])
if (currentState[key].length > 0 || previousState[key].length > 0) {
currentState[key].map((value, index) => {
// check for array of string or number or boolean
if (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'boolean'
) {
if (
JSON.stringify(currentState[key]) !==
JSON.stringify(previousState[key])
) {
result.push({
[key]: {
current: currentState[key],
previous: previousState[key]
}
});
}
}
// check for array of objects
if (typeof value === 'object') {
const ans = compare(
value,
previousState[key][index]
);
result.push(ans);
}
});
}
}
}
return result;
};
You first need a object:
const [object, setObject] = useState({
number: 0,
text: "foo"
});
You need to check when the object changed with useEffect, but you also need to see the previos object, for that we will be using a helper function.
const prevObject = usePrevious(object);
const [result, setResult] = useState("");
useEffect(() => {
if (prevObject) {
if (object.number != prevObject.number) {
setResult("number changed");
}
if (object.text != prevObject.text) {
setResult("text changed");
}
}
}, [object]);
//Helper function to get previos
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
Here is the Codesandbox
I want to create a dropdown (or mat-select) to use as a sorting mechanism instead of the Angular Material Sort Header. So, if I for example click on the 'username' inside the dropdown, I want the table to sort by the username (instead of clicking on the header).
How can I do it? Any documentation online on how to achieve this?
Thank you for any help.
As required, I attach some code:
ngOnInit(): void {
this.filteredOptions = this.myControl.valueChanges.pipe(
startWith(""),
map((value) => this._filter(value))
);
}
ngAfterViewInit() {
this.providersAdmin.sort = this.sort;
}
getAllAdmins() {
this.isLoading = true;
this.homeService.getAllAdmins().subscribe(
(response) => {
this.admins = response;
this.providersAdmin = new MatTableDataSource(this.admins);
this.isLoading = false;
},
(error) => {}
);
}
sortTableBy(event: any) {
const sortState: Sort = {
active: "username",
direction: "desc",
};
this.sort.active = sortState.active;
this.sort.direction = sortState.direction;
this.sort.sortChange.emit(sortState);
console.log(event);
}
The sortTableBy method is the one I found on here but nothing happens.
I added matSort on the mat-table and I added mat-sort-header on the header cell.
EDIT:
Hi, I managed to fix the problem by writing the following:
sortTableBy(event: any) {
const sortState: Sort = {
active: "username",
direction: "desc",
};
this.sort.active = sortState.active;
this.sort.direction = sortState.direction;
this.sort.sortChange.emit(sortState);
this.providersAdmin.sort = this.sort;
}
There is an example for you:
Exmaple
Your sort function has a wrong implementation, this is work for me:
sortData(fieldName: string) {
if (!fieldName) {
return;
}
const sortState: MatSortable = {
id: fieldName,
start: 'desc',
disableClear: true
};
this.sort.sort(sortState);
}
I am going to set up an example which you can adapt easily:
compare(a: number | string, b: number | string, isAsc: boolean) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
sortData() {
let isAsc = this.sort.direction != "" ?
event.direction == SortDirection.asc :
true;
let data = this.dataSource.data.slice();
data.sort((a, b) => {
switch (this.myChosenSort) {
case 'healthCareCenterName':
return this.compare(a.healthCareCenterName, b.healthCareCenterName, isAsc);
case 'address':
return this.compare(a.address, b.address, isAsc);
case 'contact':
return this.compare(a.contact, b.contact, isAsc);
default:
return 0;
}
});
this.dataSource = new MatTableDataSource<ServiceProviderTable>(data);
}
To change the sort.direction you need to play around a little bit with the code, maybe directly from the dropdown and hardcoding the isAsc when calling the compare method, depending on the value of the this.myChosenSort.
i creating a form with React-selec.
When i do a function and pass parameters he returns me
Expected 1 arguments, but got 0.ts(2554)
index.tsx(31, 31): An argument for 'selectRef' was not provided.
useEffect(() => {
function parseSelectValue(selectRef: { state: { value: any } }) {
const selectValue = selectRef.state.value
if (!multiple) {
return selectValue ? selectValue.id : ''
}
return selectValue
? selectValue.map((option: { id: any }) => option.id)
: []
}
registerField({
name: fieldName,
ref: ref.current as any,
path: 'state.value',
parseValue: parseSelectValue,
clearValue: (selectRef: { select: { clearValue: () => void } }) => {
selectRef.select.clearValue()
}
})
parseSelectValue()
}, [fieldName, registerField, multiple])
The function parseSelectValue expects a selectRef; Its a non-optional parameter.
function parseSelectValue(selectRef?: { state: { value: any } }) {
const selectValue = selectRef ? selectRef.state.value : undefined;
Paste this should fix the problem.
I need to apply different destructuring for the function response depending of global flag [single service for multiple apps]
// Destructuring template should be defined as value
let destructuringTemplate;
if (flag) {
destructuringTemplate = {data: {classA: user}};
} else {
destructuringTemplate = {data: {classB: user}};
}
// This technique would not work, this is just my idea representation.
this.getUser(({destructuringTemplate: user) => { this.localUser = user });
At this moment it works this way:
let destructuringTemplate;
if (flag) {
destructuringTemplate = ({data: {classA: user}}) => user;
} else {
destructuringTemplate = ({data: {classB: user}}) => user;
}
this.getUser(response => { this.localUser = destructuringTemplate(response)};
it is kinda ugly, some suggestion how it should be done?
You could use a computed property name with a conditional (ternary) operator ?:.
var flag = true,
object = { data: { classA: 'foo', classB: 'bar' } },
{ data: { [flag ? 'classA' : 'classB']: user } } = object,
{ data: { [!flag ? 'classA' : 'classB']: user1 } } = object;
console.log(user);
console.log(user1);
You don't need to use destructuring, use simple dot/bracket notation:
const userClass = flag ? 'classA' : 'classB'
this.getUser(response => { this.localUser = response.data[userClass] })
If you want to reuse this logic, just create simple function, e.g.:
const extractUser = response => response.data[userClass]
With my angular2 application, i am getting the response and assigning to object as follows,
seatingConcession: {
parking: data.concession.extras.parking ? data.concession.extras.parking : null,
restrictedview: data.concession.extras.restrictedview ? data.concession.extras.restrictedview : null,
wheelchair: data.concession.extras.wheelchair ? data.concession.extras.wheelchair : null
}
sometimes extras does not have value. sometimes restrictedview inside extras does not have value. what is the best way to check and assign the default value .
Whole code:
this.eventService.getListingsByEventId(this.eventId).subscribe(listresults => {
this.bindListing(listresults);
}, error => this.errorMessage = error);
}
bindListing(listres: any[]) {
let price_table = {};
let section_table = {};
listres.forEach((data) => {
data.ticket.seating.forEach((seat: any) => {
// tslint:disable-next-line:max-line-length
this.listings.push({
section: seat.section, selling: data.price.selling, amount: data.ticket.amount, type: data.ticket.type, row: seat.row, category: seat.category,
seatingConcession: {
parking: data.concession.extras ? (data.concession.extras.restrictedview || null) : null,
restrictedview: data.concession.extras.restrictedview || null,
wheelchair: data.concession.extras.wheelchair || null
},
deliveryconcession: {
instantdownload: data.delivery.instantdownload || null,
readytoship: data.delivery.readytoship || null,
unespecifiedshipment: data.delivery.unspecifiedshipment || null
}
});
// this.listings.push({ section: seat.section, selling: data.price.selling, amount: data.ticket.amount, type: data.ticket.type, row: seat.row, category: seat.category});
// tslint:disable-next-line:curly
if (!price_table.hasOwnProperty(data.price.selling))
price_table[data.price.selling] = [];
price_table[data.price.selling].push(data);
// tslint:disable-next-line:curly
if (!section_table.hasOwnProperty(seat.section))
section_table[seat.section] = [];
section_table[seat.section].push(data);
});
});
Service js:
getListingsByEventId(EventID: string): Observable<ListingSeller[]> {
let apiurl = this.appConfig.getAPIUrl() + '/getListingsByEventId';
return this.http
.get(apiurl + queryString)
.map(this.extractData)
.catch(this.handleErrors);
}
You can use the following function to achieve what you want.
function getSafe(fn) {
try {
return fn();
} catch (e) {
return null;
}
}
Then use it like this
seatingConcession: {
parking: getSafe(() => data.concession.extras.parking),
restrictedview: getSafe(() => data.concession.extras.restrictedview),
wheelchair: getSafe(() => data.concession.extras.wheelchair),
}
See details.
Another approach would be to execute data.concession.extras = data.concession.extras || {} before actually creating your object.
You mentioned,
"sometimes extras does not have value. sometimes restrictedview inside extras does not have value"
so, this condition will help you.
data.concession.extras ? (data.concession.extras.restrictedview || data.concession.extras ) : null
Here is an example:
The first example has restrictedview and the second example doesn't.
data = {}
data.concession = { 'extras' : {} }
data.concession.extras = { 'restrictedview' : 'restrictedview value'}
data2 = {}
data2.concession = { 'extras' : 'extras value' }
var output = data.concession.extras ? (data.concession.extras.restrictedview || data.concession.extras ) : null
var output2 = data2.concession.extras ? (data2.concession.extras.restrictedview || data2.concession.extras ) : null
console.log(output)
console.log(output2)
PLEASE RUN THE ABOVE SNIPPET
Observables do try...catch, so for data structures it is possible to follow the pattern:
data$
.map(data => data.complex.path || null)
.catch(() => Observable.of(null))
But for nested structures this will result in complex observable hierarchy which is hard to comprehend.
So basically it is possible to treat complex paths to values with this recipe:
parking: ((data.concession || {}).extras || {}).parking || null
This case is conveniently treated by Lodash/Underscore get or a similar helper function:
parking: _.get(data, 'concession.extras.parking', null)