I want to validate the object so that the object is not null and few of the fields in that object is not empty.
For example, consider the following object:
address ={
block : '2134',
street : 'berly street',
county : 'someCountry'
postal : '876546'
}
I am using es6. I found the solution to do this using lodash
I want to do this in plain JS.
Please let me know if there is a better way to do this than below:
const isValidAddress = (address) => {
const addressFields = ['block', 'county', 'postal', 'street'];
return addressFields.every((element) => address[element] && address[element].length > 0);
};
You can try checking every value with Array.prototype.every like this.
const invalidAdress ={
block: '',
street: 'berly street',
county: 'someCountry',
postal: '876546'
};
const validAdress ={
block: '1234',
street: 'berly street',
county: 'someCountry',
postal: '876546'
};
// .every will return true if and only if every property in the object is a truthy value
const isValidAddress = (address) => Object.keys(address).every(x => address[x]);
console.log(isValidAddress(invalidAdress));
console.log(isValidAddress(validAdress));
Related
I am being passed an object containing address data (in alphabetical order) that I am wanting to extract a sub-set of to display, but in a different order.
Not all of the keys will have data in all of the time, sometimes having an empty string instead of nothing at all.
const address = {
buildingName: '',
company: 'My org',
county: 'My County',
postCode: 'My Postcode',
streetName: 'My street',
townCity: 'My Town'
};
I know that I can get all of the present values out by doing:
Object.keys(address).filter(Boolean).join(', ')
However I am wanting the order of the information being output to be: company, buildingName, streetName, townCity, county, postCode.
Is there any way in which I can alter my current solution, or do I need a different approach entirely?
I managed to get a solution combining Jan Pfeifer and Gog's solutions to some extent, along with my own experimentation.
It also transpired that fields with no value weren't being returned as empty strings, but weren't included, so that made things a bit easier.
const ADDRESS_ORDER = ['company', 'buildingName', 'streetName', 'townCity', 'county', 'postCode'];
return const addressString = ADDRESS_ORDER
.filter(detail => Object.keys(address).includes(detail))
.map(key => address[key])
.join(', ');
const ADDRESS_ORDER = ['company', 'buildingName', 'streetName', 'townCity', 'county', 'postCode'];
const address = {
company: 'My org',
county: 'My County',
postCode: 'My Postcode',
streetName: 'My street',
townCity: 'My Town'
};
const result = ADDRESS_ORDER
.filter(detail => Object.keys(address).includes(detail))
.map(key => address[key])
.join(', ');
console.log({result});
If this was any longer or more complex, I would consider tidying up by combining the fileter and map into a reduce, but I don't think it warrants this at the moment.
Because you need custom order you will have to create that object manually. You can use template array, but that is more or less the same solution.
const address = {
buildingName: '',
company: 'My org',
county: 'My County',
postCode: 'My Postcode',
streetName: 'My street',
townCity: 'My Town'
};
const tmp = ["company", "buildingName", "streetName", "townCity", "county", "postCode"];
let s = "";
tmp.forEach(key => {
const v = address[key];
if(v) {
if(s) s += ", ";
s += v;
}
});
console.log(s);
You could create an addressOrder array, to specify the order of the various address components, then use Array.reduce() to add to the output address components.
We'd only add the address to the output if it's present in the address object.
Finally we'd join the address components using the provided delimiter.
const address = {
buildingName: 'Nakatomi Towers',
company: 'Nakatomi Corp.',
county: 'LA County',
postCode: '90067',
streetName: 'Century City',
townCity: 'LA'
};
function formatAddress(address, addressOrder, delimiter = ', ') {
return addressOrder.reduce((outputFields, field) => {
if (address[field]) {
outputFields.push(address[field]);
}
return outputFields;
}, []).join(delimiter);
}
const order1 = ['company','buildingName','streetName','townCity','county','postCode']
const order2 = ['buildingName','streetName','townCity','postCode']
console.log('Address:', formatAddress(address, order1))
console.log('\nAddress (alt. format):\n' + formatAddress(address, order2, '\n'))
.as-console-wrapper { max-height: 100% !important; }
Have long JSON object and small part is below:
const valueObject = {zipCode: 12345, street1: 'street 1', street2: 'street 2',city:'cityname', uniqueNum: '123456789'};
const mappingObject = {address: {pinCode: zipCode, addressLine1: 'street1', addressLine2:'street2', id: 'uniqueNum', city: city}
const formatObject = {address: {pinCode: '', addressLine1: '', addressLine2:'', id: '0', city: ''}
Trying to transform valueObject to formatObject using mappingObject.
(if valueObject has no value then use default value from formatObject)
It doesn't get work. Seems have a object and not an array. Not sure what is wrong here, Or does it need to merge.
Object.keys(formatObject).map((k)=>{k: valueObject[mappingObject[k]] || mappingObject[k]});
Output expected:
{address: {pinCode: 12345, addressLine1: 'street 1', addressLine2: 'street 2',city:'cityname', id: '123456789'};
In case, any better option through ES6 (
Else case, using for loop and prepare object. )
You actually do not need the formatObject here because the mappingObject gives the full transformation schema, i.e. the relation between two structures. formatObject merely serves as an example of what the possible output looks like.
A feasible implementation would be:
function transform(schema, x) {
return Object.fromEntries(Object.entries(schema).map(([k, v]) => {
if (typeof v === 'string') return [k, x[v]]
else if (typeof v === 'object') return [k, transform(v, x)]
else throw new TypeError(`Schema entry cannot have type ${typeof v}`)
}))
}
transform(valueObject, mappingObject)
that works recursively through the schema to transform a flat-structured object (in this case valueObject) to a nested-structured object.
I got a function:
const doSomething= (person) => {
// Some code...
}
that gets this object as a parameter:
const person = {
name: 'Daniel',
Age: 34,
City: 'Dresden'
}
To handle a case where city is undefined,
how can I give only this specific city key default value? Not the entire object like this:
const doSomething= (person = null) => {
// Some code...
}
Instead of using a person argument, destructure it into name,, Age, and City, and assign a default value to City:
const doSomething = ({ name, Age, City = 'default city' }) => {
// Some code...
}
I agree with #CertainPerformance answer; or you can do in the function
person.City = person.City || ''
putting instead of '' the default value you want. Leaving '' makes City a string with default value empty string.
So I'm having this little generator, the info that get displayed should correspond to what values are populated on the database, the example below is a physical address
this.setState({
companyEmail: json.data.companyEmail,
company: json.data.company,
line1: json.data.line1,
line2: json.data.line2,
city: json.data.city,
...
});
So I could initialize those values with null, undefined or "" it does not matter, on the fetch request the typeof those empty values turn to undefined.
Firebase don't like that, for me it cause no issue, I could hide the element by checking if it's true or false like so
{line1 && <span>{line1}<br /></span>}
{line2 && <span>{line2}<br /></span>}
Well I mean it works until I tried updating the database, I got this error
Error: Reference.set failed: First argument contains undefined in
property 'master.business.info.line2'
What I want to do is kindof turning undefined to null, because when sending null remove the child from the db, it's treated like a real value.
On the server side, it's regular db update
master.update({
company: company,
line1: line1,
line2: line2,
city: city,
...
})
.then(() => {
response.sendStatus(200);
}).catch(function (err) {
console.log(err);
response.sendStatus(204);
});
I could use if statement and setState accordingly but that's too much work. I need to make to fetch request because I need those values, it's a form to edit info.
Also using empty strings like "" is not good, it gets display inside span, paragraph or whatever.
This is not a duplicate, on react.js my goal is to not setState for undefined values and keep the init values on the constructor.
Maybe if you change your setState like this:
const {companyEmail, company, line1, line2, city} = json.data;
this.setState({
companyEmail,
company,
line1,
line2,
city,
...
});
If any var doesn't have value, it doesn't add to state.
Here, you could check before update. Try something like this:
const objUpdate = {
company: company,
line1: line1,
line2: line2,
city: city,
...
};
const cloneObjUpdate = {...objUpdate};
Object.keys(objUpdate).forEach(key=>{
if(!Boolean(cloneObjUpdate[key])) delete cloneObjUpdate[key];
});
master.update(cloneObjUpdate);
For keep object with null values, just remove delete and set a null value:
const objUpdate = {
company: company,
line1: line1,
line2: line2,
city: city,
...
};
const cloneObjUpdate = {...objUpdate};
Object.keys(objUpdate).forEach(key=>{
if(!Boolean(cloneObjUpdate[key])) cloneObjUpdate[key] = null;
});
I started with changing undefined to null for Firebase’s sake, but then I realized it was easier to just remove the undefined keys altogether:
const removeUndefinedKeys = (collection) => Object.keys(collection).reduce((result, key) => (
![undefined, null].includes(collection[key])
? { ...result, [key]: collection[key] }
: result
), {})
I have 2 sources of data. One of the sources is the "template" to what is acceptable for the data. However, the second source may have a large amount of data that I don't care about (100+ properties in the JSON). Here are the schemas:
// Only store the data we care about. Only a small subset of
// data that I need for this particular dataset.
state = {
isDirty: false,
data: {
name: '',
address: '',
city: '',
state: ''
}
}
The second source will have the 4 attributes in the data schema above (plus many many more I don't care about). Currently, I am assigning them like this:
let updatedData = {};
for(const key in this.state.data) {
updatedData[key] = someDataSource[key];
}
this.state.data = updatedData;
Using ES6, and perhaps destructing, is there a better way to mass assign variables like this?
Thanks again!
EDIT
Added for clarification the assignment after the loop.
Lodash pick can be used to pick specific keys, or helper function can be used for same purpose:
const pick = (obj, keys) => Object.keys(obj)
.filter((key) => keys.indexOf(key) >= 0)
.reduce(
(newObj, key) => Object.assign(newObj, { [key]: obj[key] }),
{}
);
This is already suggested in many related questions. The thing that is specific to this question is:
this.state.data = pick(someDataSource, Object.keys(this.state.data));
Properties can be excluded and modified in the JSON.parse reviver :
var o = JSON.parse('{"a":1, "b":2}', (k, v) => k === 'a' ? void 0 : k === 'b' ? 3 : v)
console.log( o )
A trick you can do (trick because it requires to swallow an error) is to use an non extensible object, using the Object.preventExtensions and then use Object.assign to fill it with data (in a try/catch block).
// Only store the data we care about. Only a small subset of
// data that I need for this particular dataset.
state = {
isDirty: false,
data: {
name: '',
address: '',
city: '',
state: ''
}
}
const newData = {
name:'name',
address:'address',
city:'city',
state:'state',
phone:'phone',
zip:'zip'
}
const updatedData = Object.preventExtensions({...state.data});
try{
Object.assign(updatedData, newData);
} catch(throwaway){};
console.log(updatedData);
And as a function for reuse
function schemaMerge(schema, data) {
const mergedData = Object.preventExtensions({...schema});
try {
Object.assign(mergedData, data);
} catch (throwaway) {};
return ({...mergedData}); // create a new object from the merged one so that it no longer is extensionless
}
// Only store the data we care about. Only a small subset of
// data that I need for this particular dataset.
state = {
isDirty: false,
data: {
name: '',
address: '',
city: '',
state: ''
}
}
const newData = {
name: 'name',
address: 'address',
city: 'city',
state: 'state',
phone: 'phone',
zip: 'zip'
}
const updatedData = schemaMerge(state.data, newData);
state.data = updatedData;
console.log(state.data);