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; }
Related
I'm working on an application that involves a somewhat complex user registration. Something similar to this.
const [data, setData] = useState({
identity: '',
people: [{
name: '',
address: [{
street: '',
city: ''
}]
}]
})
function addAddress(){
setData({
...data,
people: [
...data.people,
{
address: [
...data.people[0].address,
{
street: '',
city: ''
}
]
}
]
})
}
When a user adds a new address to a person he is registering, he should add a new address to the person and keep the previous data. But it creates a new data array with just the address data, outside of the specified person.
Could someone help me how to do this insertion into the array?
It's not the best solution i guess, but it should work
I'm using here JSON.stringify() and JSON.parse() to deep copy your previous data
function addAddress (newAddress) => {
setData((previousData) => {
let newData = JSON.stringify(previousData);
newData=JSON.parse(newData);
newData.people[0].address.push(newAddress);
return (newData);
});
}
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 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));
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);
I have given an array of document id's which I want to add to Firestore.
let documentIds = [
'San Francisco',
'New York',
'Las Vegas'
]
Each document should have a predefined set of properties.
let data = {
country: 'US',
language: 'English'
}
Is there some function inside the Firebase Admin SDK that can create all documents at once, without iterating through the array of documentIds?
This should help. Sam's answer will give you a good understanding of how to use batch writes. Be sure to check out the documentation that he linked to.
I don't want to upset Sam. He fixed my laptop, yesterday :-)
Setting a batch with an array
let documentIds = [
'San Francisco',
'New York',
'Las Vegas'
]
let data = {country: 'US', language: 'English'}
var batch = db.batch();
documentIds.forEach(docId => {
batch.set(db.doc(`cities/${docId}`), data);
})
batch.commit().then(response => {
console.log('Success');
}).catch(err => {
console.error(err);
})
You can do a batched write. You'll still need to iterate through the docs to add them all to the batch object, but it will be a single network call:
Example:
// Get a new write batch
var batch = db.batch();
// Set the value of 'NYC'
var nycRef = db.collection("cities").doc("NYC");
batch.set(nycRef, {name: "New York City"});
// Update the population of 'SF'
var sfRef = db.collection("cities").doc("SF");
batch.update(sfRef, {"population": 1000000});
// Delete the city 'LA'
var laRef = db.collection("cities").doc("LA");
batch.delete(laRef);
// Commit the batch
batch.commit().then(function () {
// ...
});
https://firebase.google.com/docs/firestore/manage-data/transactions#batched-writes