What is the better way to check in array of nested object - javascript

I'd like to check at least one checked field is true and not disabled:
const test =
[ { Title:
{ Article: { checked: true, disabled: true }
, Desc: { checked: false, disabled: false }
} }
, { News:
{ Dashboard: { checked: false, disabled: false}
} }
]
I tried like this:
const checkedItems = () => {
let value = false;
test.forEach(el => Object.entries(el).forEach(([title, checkboxProps]) => {
Object.entries(checkboxProps).forEach(([name, config]) => {
if (config["checked"] && !config["disabled"]) {
value = true
}
})
}))
return value;
};

A couple of flatMaps with Object.values can do it pretty cleanly.
const test = [{
Title: {
Article: {
checked: true,
disabled: true
},
Desc: {
checked: false,
disabled: false
}
}
}, {
News: {
Dashboard: {
checked: false,
disabled: false
}
}
}];
const atLeastOneCheckedAndNotDisabled = test
.flatMap(Object.values)
.flatMap(Object.values) // now we have an array of [{checked, disabled}]
.some(innerObj => innerObj.checked && !innerObj.disabled);
console.log(atLeastOneCheckedAndNotDisabled);
You don't care about the keys, only the values, so Object.values will make things easier to work with than Object.entries.

Related

Disable selected options in ng-multiselect-dropdown

List used for the nf-multiselect-dropdown :
children: any = [{
id: "Son",
label: 'Son',
isDisabled: false
}, {
id: "Daughter",
label: 'Daughter',
isDisabled: false
}, {
id: "None",
label: 'None',
isDisabled: false
}];
Dropdown settings :
this.dropdownSettingsChildren = {
singleSelection: false,
idField: 'id',
textField: 'label',
selectAllText: 'Select All',
unSelectAllText: 'Unselect All',
itemsShowLimit: 1,
allowSearchFilter: true,
enableCheckAll: false,
};
Logic : When selected the 'None' option, it should make the fields 'isDisabled' as true
onChildrenOptionsSelect(event: any) {
if (event.id.includes('None')) {
for (let ele of this.children) {
if (!(ele.id.includes('None'))) {
ele.isDisabled = true;
}
}
}
this.onChildrenOptionsSelectOperation();
}
onChildrenOptionsDeSelect(event: any) {
if (event.id.includes('None')) {
for (let ele of this.children) {
if (!(event.id.includes('None'))) {
ele.isDisabled = false;
}
}
}
this.onChildrenOptionsSelectOperation();
}
HTML code ->
<ng-multiselect-dropdown class="width-120"
[placeholder]="'Select'" ngDefaultControl
[settings]="dropdownSettingsChildren" [data]="children"
[(ngModel)]="psHistory.maritalStatus.children"
name="dummyname"
(onSelectAll)="onChildrenOptionsSelectAll($event)"
(onDeSelectAll)="onChildrenOptionsSelectAll()"
(onSelect)="onChildrenOptionsSelect($event)"
(onDeSelect)="onChildrenOptionsDeSelect($event)">
</ng-multiselect-dropdown>
When checked the array, the values are properly reflecting but the options in ng-multiselect-dropdown are not disabled
I'd like to reflect the disabled fields on UI as well
I used this link as a reference to my code -> Stackblitz reference

Compare array of object with an array

I have a request
const request=additionalFields: [
{ field: 'PHONE', required: true },
{ field: 'COMPANY', required: true },
{ field: 'MOBILE', required: true },
{ field: 'COUNTRY', required: true }
]
the response i am getting is this
"additionalFields":
{
'PHONE':true,
'COMPANY':true,
'MOBILE':true,
'COUNTRY':true
}
i am not sure how to compare them. i kind of wrote something but even to me it looks wrong
const fields = request["additionalFields"];
for (let i = 0, len = fields.length; i < len; i++) {
expect(request.additionalFields[i].required).to.eql(
response.additionalFields
);
Here's another way. Just filter out anything that doesn't match up, then you'll have an array of differences if there are any.
const request={
additionalFields: [
{ field: 'PHONE', required: true },
{ field: 'COMPANY', required: true },
{ field: 'MOBILE', required: true },
{ field: 'COUNTRY', required: true }
]
}
const incoming = {
"additionalFields": {
'PHONE': true,
'COMPANY': true,
'MOBILE': false,
'COUNTRY': true
}
}
let differences = request.additionalFields.filter(f => incoming.additionalFields[f.field] !== f.required)
console.log(differences)
const request = [
{ field: 'PHONE', required: true },
{ field: 'COMPANY', required: true },
{ field: 'MOBILE', required: true },
{ field: 'COUNTRY', required: true }
]
const response = {"additionalFields":
{
'PHONE':true,
'COMPANY':true,
'MOBILE':true,
'COUNTRY':true
}}
for (const [key, value] of Object.entries(response['additionalFields'])) {
const {required, field} = request.find(({field}) => key === field);
if(required === value) { // here you can compare
console.log(key, field, value);
}
}
You need to dereference the response.additionalFields object using the value of field from each entry in request.additionalFields. Something like
for(const { field, required } of request.additionalFields) {
expect(required).to.eql(
response.additionalFields[field]
);
}
You should loop over each field detail in the request and compare the value against the response. Here you go:
const request = {
additionalFields: [
{ field: 'PHONE', required: true },
{ field: 'COMPANY', required: true },
{ field: 'MOBILE', required: true },
{ field: 'COUNTRY', required: true },
],
};
const response = {
additionalFields: {
PHONE: true,
COMPANY: true,
MOBILE: true,
COUNTRY: true,
},
};
for (let f of request.additionalFields) {
// Observe f is like { field:'PHONE', required: true }
// Next two lines are just for test, please remove and uncomment last one in your use case.
const comparisonResult = f.required === response.additionalFields[f.field];
console.log(`for field ${f.field}, It's ${comparisonResult}`);
// expect(f.required).to.eql(response.additionalFields[f.field]);
}
You can further improve readability by using destructuring, drop a comment if you want to see that.
You can use the for...of syntax to do this.
Here's the documentation.
const request = {
additionalFields: [
{ field: 'PHONE', required: true },
{ field: 'COMPANY', required: true },
{ field: 'MOBILE', required: true },
{ field: 'COUNTRY', required: true }
]
};
const response = {
additionalFields: {
PHONE:true,
COMPANY:true,
MOBILE: true,
COUNTRY:true
}
}
const requestFields = request['additionalFields'];
const responseFields = response['additionalFields'];
for(const { required, field } of requestFields) {
expect(required).to.eql(responseFields[field]);
}

ReactJS - Function to bulk overwrite nested usestate variable

I have a nested dictionary object with the following structure:
const [fieldErrorMessage, setFieldErrorMessage] = useState({
signinEmail: {
show: false,
message: "Error",
},
signinPassword: {
show: false,
message: "Error",
},
signupEmail: {
show: false,
message: "Error",
},
signupPassword: {
show: false,
message: "Error",
},
signupRegistrationToken: {
show: false,
message: "Error",
},
});
I created a function which should take in an array of nested dictionaries (e.g., signinEmail, signinPassword, ...) and set their value for their show key to false (without changing the value for the message property).
Here is my function:
function hideFieldErrors(setter, fieldNameArray) {
fieldNameArray.forEach((fieldName) => {
setter((prevState) => ({
...prevState,
fieldName: {
...prevState.fieldName,
show: false,
},
}));
});
}
When I call it, the show values do not change. Why is that?
I'm calling it via this code:
hideFieldErrors(setFieldErrorMessage, [
"signinEmail",
"signinPassword",
"signupEmail",
"signupPassword",
"signupRegistrationToken",
]);
The way you have used fieldName is not working as you expected. variable name(which is a string) should be wrapped with square brackets to make it an accessible property.
Try like below.
function hideFieldErrors(setter, fieldNameArray) {
fieldNameArray.forEach((fieldName) => {
setter((prevState) => ({
...prevState,
[fieldName]: {
...prevState[fieldName],
show: false,
},
}));
});
}

Object values not updating javascript

const displayDataTypes = [];
const TypeMap = {
data: ['type2', 'type3'],
};
const Type = {
title: 'Type',
values: [
{ label: 'Type-1', value: 'type1', disabled: false },
{ label: 'Type-2', value: 'type2', disabled: false },
{ label: 'type-3', value: 'type3', disabled: false },
{ label: 'type-4', value: 'type4', disabled: false },
{ label: 'type-5', value: 'type5', disabled: false }
]
};
const TypesSelection = TypeMap['data'];
Type.values.forEach((item) => {
const tmp = Object.create(item);
TypesSelection.forEach((type) => {
if (tmp.value !== type) {
tmp.disabled = true;
}
});
displayDataTypes.push(tmp);
});
console.log(displayDataTypes);
In the above code, Every object property disabled is getting true. I need the type2 and type3 should be false and rest should be true because of TypeMap - data.
So the output should be
[
{ label: 'Type-1', value: 'type1', disabled: true },
{ label: 'Type-2', value: 'type2', disabled: false },
{ label: 'type-3', value: 'type3', disabled: false },
{ label: 'type-4', value: 'type4', disabled: true },
{ label: 'type-5', value: 'type5', disabled: true }
]
Try it using the Array.map function:
const output = Type.values.map(v => {
v.disabled = !TypesSelection.includes(v.value);
return v;
})
console.log(output)
This is not only smaller but more readable.
The learn more about the Array.map function: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
The issue is in the iteration. At some point tmp.value will be 'type2' and type will be 'type3'. This will cause disabled to get set to true for the 'type2' object.
I think this does what you want.
const Type = {
title: 'Type',
values: [
{
label: 'Type-1',
value: 'type1',
disabled: false,
},
{
label: 'Type-2',
value: 'type2',
disabled: false,
},
{
label: 'type-3',
value: 'type3',
disabled: false,
},
{
label: 'type-4',
value: 'type4',
disabled: false,
},
{
label: 'type-5',
value: 'type5',
disabled: false,
},
],
};
const TypeMap = {
data: ['type2', 'type3']
};
const TypesSelection = TypeMap['data'];
const displayDataTypes = Type.values.map(item => {
const shallowCopy = {...item};
if (TypesSelection.every(type => (type !== shallowCopy.value))) {
shallowCopy.disabled = true;
}
return shallowCopy;
});
console.log(displayDataTypes);
I would recommend not using a property named "disable" because it causes confusing double negative situations. Instead use "enabled". This is of course assuming you have control of the property names.
If you don't need to preserve the initial "disabled" states on the copies and if you don't need copies then use the much smaller / simpler implementation in Ilijaz's answer.
Your code is failing because the correct result is being overwritten by other iterations of your inner loop.
Your current structure is 2-step:
Loop over each value in Type.values
Loop over each value in TypeMap.data and check if the two elements don't match
As a result of that second step, even elements that match will get compared to another element that doesn't match, causing the conditional code to get executed anyway.
Eg.:
Outer loop:
item is type2
Inner loop:
type is type2
check the condition: type2 !== type2 is false
Skip conditional code
Inner loop
type is now type3
check the condition: type3 !== type3 is true
run conditional code, set disabled to true
As such, all disableds will always be set to true.
Using a nested forEach isn't very well suited for this use case, since there's no straightforward way to avoid this problem. If you still want to use forEach for the outer loop, you can, or you could simplify further with map(), as #Ilijaz suggested in another answer.
Using forEach and Array.prototype.includes:
const displayDataTypes = [];
const TypeMap = {
data: ['type2', 'type3'],
};
const Type = {
title: 'Type',
values: [{
label: 'Type-1',
value: 'type1',
disabled: false,
},
{
label: 'Type-2',
value: 'type2',
disabled: false,
},
{
label: 'type-3',
value: 'type3',
disabled: false,
},
{
label: 'type-4',
value: 'type4',
disabled: false,
},
{
label: 'type-5',
value: 'type5',
disabled: false,
},
],
};
const TypesSelection = TypeMap['data'];
Type.values.forEach((item) => {
const tmp = Object.create(item);
if (!TypesSelection.includes(tmp.value)) {
tmp.disabled = true;
}
displayDataTypes.push(tmp);
});
console.log(displayDataTypes);
.as-console-wrapper {
max-height: 100% !important;
}

Best way to loop inside an object with the key being a value inside the object

I have this state in react as following
state = {
profile: {
name: { value: '', isRequired: true, hasError: false, order:1 },
industry: { value: '', isRequired: false, hasError: false, order:2 },
address: { value: '', isRequired: false, hasError: false. ordere:3},
crn: { value: '', isRequired: true, hasError: false, order:4 },
website: { value: '', isRequired: false, hasError: false, order:5 },
employeesNbr: { value: '', isRequired: true, hasError: false, order:6 },
phoneNumber: { value: '', isRequired: true, hasError: false, order:7 },
userRole: { value: '', isRequired: true, hasError: false, order:8},
personCheck: { value: false, isRequired: true, hasError: false, order:9 },
},
};
And I have the following logic that was using the profile values as keys
handleInputChange = (key: string, value: string) => {
const { profile } = this.state;
profile[key].value = value;
profile[key].hasError = false;
this.setState({ profile });
};
handleProfileFormSubmit = () => {
const { profile } = this.state;
let errorExists = false;
const arr = [];
Object.keys(profile).forEach((key: string) => {
const element = profile[key];
if (
(typeof element.value !== 'boolean' &&
element.isRequired &&
isEmpty(element.value)) ||
(element.isRequired && !element.value)
) {
element.hasError = true;
errorExists = true;
arr.push(key);
}
});
How can I change the handleProfileFormSubmit logic to go through the foreach loop using the order as keys so it goes through them from lower to higher number. This is important because changing the input fields in the front end without changing the order in the state part makes undefined errors when passing values?
For that you will have to do a sort first. Instead of Object.keys(profile).forEach do this.
let sortedKeys = Object.keys(profile).sort((a, b)=> profile[a].order - profile[b].order)
sortedKeys.forEach((key: string) => {
const element = profile[key];
})

Categories

Resources