I have an excel file (is quite large) as follows:
Region Country City
North America United States Kentucky
North America United States New York
... All cities in US
North America Canada Alberta
... All cities in Canada
Europe France Nice
... All cities in France
I basically want to convert this to a front-end form. So I would have 3 inputs (one for region, one for country, and one for city). Now, I would like a way where if a user selects North America as a region, I only allow them to select countries within North America, and when a user selects a country, only show them cities within a country.
My current approach to tackle countries within a region is quite simple since there are not many regions:
Main Function - content
function content(){
const [regionValue, setRegionValue] = React.useState();
const [countryValue, setCountryValue] = React.useState();
const [cityValue, setCityValue] = React.useState();
...Render components
<RegionSelect regionValue={regionValue} setRegionValue={setRegionValue}></RegionSelect>
<CountrySelect countryValue={countryValue} setCountryValue={setCountryValue} regionValue={regionValue}></CountrySelect>
}
Function for Region:
export const REGIONS = [
{ label: 'North America', value: 'North America' },
{ label: 'South America', value: 'South America' },
{ label: 'Europe', value: 'Europe' }
...
];
function RegionSelect(props){
return (
<Select options = {REGIONS} value = props.regionValue onChange = props.setRegionValue /> //My custom component which just allows user to select and changes the value accordingly
)
}
Function for Country:
const NA_COUNTRIES = [
{ label: 'Canada', value: 'Canada' },
{ label: 'United States', value: 'United States' },
{ label: 'Mexico', value: 'Mexico' }
]
const SA_COUNTRIES =....
function CountrySelect(props){
//since I passed in region to props, I can do an if condition to fill select statement with refined options:
if (props.regionValue == 'North America'){
<Select options = {NA_COUNTRIES} value = props.countryValue onChange = props.setCountryValue />
}
else if..... #Else if statements for every region to display other options
}
Now the countries are quite easy since there aren't many regions but the cities is where it becomes a bit more difficult and tedious. Is it possible for me to do this a better way (maybe by directly reading a csv file in react and doing a match/join)?
Related
I'm trying to write code that will allow user to check to see if I have been to a specific country.
I've tried this by getting input from user via prompt. Taking that input, storing it into a variable and then inserting that variable into a regex. From there, I attempted to use the .some method on my array of countries, utilizing the regex and the user input variable to determine whether or not I have been to the country that the user specifies.
var countries = ['US', 'UK', 'Canda', 'Mexico', 'Panama', 'Dominican
Republic', 'Brazil', 'Germany', 'France', 'Portugal',
'Spain', 'the Netherlands'];
var userCountry = prompt("Please enter a country", "");
var beenToUserCountry = countries.some(country =>
/^userCountry$/i.test(country));
if (beenToUserCountry) {
document.write(`Yes, I have been to ${userCountry}.`);
} else {
document.write(`No, I haven't been to ${userCountry} yet.`);
}
I expect that the code will print "Yes..." for countries that a part of my countries array and and "No..." for ones that are not. Instead, each country that I insert into the prompt gets me a "No..." Help!
You can use a RegExp object for that, here is an example:
var countries = ['US', 'UK', 'Canda', 'Mexico', 'Panama', 'Dominican Republic', 'Brazil', 'Germany', 'France', 'Portugal',
'Spain', 'the Netherlands'];
var userCountry = prompt("Please enter a country", "");
var beenToUserCountry = countries.some(country =>
new RegExp(`^${userCountry}$`, "i").test(country));
if (beenToUserCountry) {
document.write(`Yes, I have been to ${userCountry}.`);
} else {
document.write(`No, I haven't been to ${userCountry} yet.`);
}
As #Bergi has mentioned, you should probably escape userCountry to remove any special characters from it, you can find a good example of how you can do that here
There will be multiple excel files which has the same fields but they might be aligned differently. Such as in first excel file the "price" column might be at the first sequence but at the 2nd file it might be in the 3rd sequence.
So if I were to ask to user to enter sequences under the name of fields (so I'll know the order of fields), could I only convert those fields in the order that I want to JSON with Javascript or Nodejs?
If so, how?
Example:
This is client no. 1's data: stored in this orientation
https://imgur.com/yIgOF8w
This is client no. 2's data: stored in this orientation. 1 extra data that I won't use and different than the first one.
https://imgur.com/lY96c7J
And I want to parse it exactly as how client no. 1 stored. But there are so many varieties that I'll get. So as I explained above, if I were to get the column numbers of certain fields could I get it in the exact format with first client and transform to JSON like that.
You can use the module excel js for this purpose, it has a lot of nice features.
I've updated to allow passing the column order to the readValues function.
For example:
var excel = require('exceljs');
var wb = new excel.Workbook();
var path = require('path');
var client1Path = path.resolve(__dirname, 'client1_data.xlsx');
var client2Path = path.resolve(__dirname, 'client2_data.xlsx');
function readValues(filePath, columnNumbers) {
let columnNames = Object.entries(columnNumbers).reduce((a, [key,value]) => {
a[value] = key;
return a;
}, []);
return wb.xlsx.readFile(filePath).then( () => {
var worksheet = wb.getWorksheet("Sheet1");
var values = [];
worksheet.eachRow((row) => {
let obj = row.values.reduce((o, v, index) => {
if (columnNames[index]) o[columnNames[index]] = v;
return o;
}, {});
values.push(obj);
});
return values;
});
}
async function testReadData() {
try {
let client1Data = await readValues(client1Path, { price: 1, name: 2, country: 3});
console.log('testReadData: Client 1 data: ', client1Data);
let client2Data = await readValues(client2Path, { price: 2, name: 1, country: 4});
console.log('testReadData: Client 2 data: ', client2Data);
} catch (error) {
console.error('testReadData: Error occurred: ', error);
}
}
testReadData();
I'm using the same data as in your examples (now corrected).
e.g.
Client 1 data:
$50 Jack USA
$30 Harold USA
Client 2 data:
Jack $50 Florida USA
Harold $30 California USA
The output will be like:
testReadData: Client 1 data:
[ { price: '$50', name: 'Jack', country: 'USA' },
{ price: '$30', name: 'Harold', country: 'USA' } ]
testReadData: Client 2 data:
[ { name: 'Jack', price: '$50', country: 'USA' },
{ name: 'Harold', price: '$30', country: 'USA' } ]
I receive data / an array with objects. each object has country - city and shops (in that city).
For example:
USA - Los Angeles - shop1
Italy - Milan - shop2
Italy - Rome - shopXY
var data = [
{country: "Italy", city: "Milan", shop: "shop123"},
{country: "Italy", city: "Rome", shop: "shop2"},
{country: "USA", city: "Los Angeles", shop: "shopXY"}
]
I have 3 columns and I want to show only once Italy, then when I click on it, I show Milan and Rome, then based on whether I click Rome or Milan the corresponding shops.
Would it be good practise to:
get all the data, then create a new array of objects, so I dont have duplicate countries, cities etc.
Use filter method- but how could I filter it, how can check for duplicates
without storing them in a new array or the like?
Anything else?
I tried to research but couldn't really find anything and even if someone just has some tips, it would be great as I'm totally lost. Thanks!!!!
PS. I don't want to use any filter libraries for that as I m trying to do it myself.
I would recommend using a javascript object, as opposed to making a new array, with the processed data.
The property/value semantics of a Javascript object have following advantages
1.) Duplicates are automatically taken care of: Using the country as a key, you either create a new sub-object mapping shops to cities, or you simply expand an already present one
2.) Ease of access: Finding a Country/shop becomes as simple as data['Italy']['Rome']
Seems like a perfect fit for your use-case. The static way of defining such an object, in adherence to your example woud be:
var data = {
"Italy": {
"Milan": ["shop123"],
"Rome": ["shop2"]
},
"USA": {
"Los Angeles": ["shopXY"]
}
}
MTTI answers is good, and to get to it, I'd use a reduce
const newData = data.reduce((acc, val) => {
if (!acc[val.country]) {
acc[val.country] = { [val.city]: [val.shop] };
return acc;
}
if (!acc[val.country][val.city]) {
acc[val.country][val.city] = [val.shop];
return acc;
}
acc[val.country][val.city].push(val.shop);
return acc;
}, {})
So in the end you'll get an object like
{
Italy: {
Rome: ['Shop123'],
Milan: ['Shop12']
},
USA: {
"San Francisco": ['Shop420']
}
}
You can use the following script to filter the data after that you can iterate on the result of the script to show the data as you want.
var data = [{
country: "Italy",
city: "Milan",
shop: "shop123"
},
{
country: "Italy",
city: "Rome",
shop: "shop2"
},
{
country: "USA",
city: "Los Angeles",
shop: "shopXY"
},
]
var result = {};
data.forEach(function (item) {
if (result[item.country]) {
result[item.country][item.city] = item.shop
} else {
result[item.country] = {};
result[item.country][item.city] = item.shop
}
});
The out put will be like -
{
"Italy": {
"Milan": "shop123",
"Rome": "shop2"
},
"USA": {
"Los Angeles": "shopXY"
}
}
I'm working on a search function that is getting passed through an array filter, followed by a reduce to match the correct strings to each other but I'm experiencing some difficulties getting the expected results.
I have an enum of common phrases that I want to map to their values, for instance if Amer is searched it should also match American. To achieve this I've setup an object with some values with a reduce that should add the value to the search string, for instance if either the API data or the search data contains U Street African Amer it should turn it into u street african amer american in the return. If it doesn't find any mapped data it should pass back the original function arguement.
const acronymEnum = {
MT: 'Mountain',
AMER: 'American',
PL: 'Place',
UDC: 'Univeristy of the District of Columbia',
AU: 'American University',
AVE: 'Avenue',
CUA: 'Catholic University of America',
NOMA: 'North of Massechusets Avenue',
GMU: 'George Mason University',
VT: 'Virginia Tech',
UVA: 'University of Virginia',
RAEGAN: 'DCA',
ST: 'Street',
SW: 'South West',
SQ: 'Square',
PENN: 'Pennsylvania',
};
function convertStationAcronym(stationName) {
return Object.keys(acronymEnum).reduce((previous, current) => {
if (stationName.toLowerCase().includes(current.toLowerCase())) {
console.log('trigger the match', current)
return stationName.concat(` ${acronymEnum[current]}`).toLowerCase();
} else {
return previous.toLowerCase();
}
}, '');
}
console.log(convertStationAcronym('U Street African Amer'))
The issue I'm running into is that it's returning either undefined, or it's adding only one mapped phrase to the return depending on what is passed into the function. Any help would be appreciated!
You will want to start the reduce with stationName as the initial value, and then always concatenate to previous not the original stationName value:
const acronymEnum = {
MT: 'Mountain',
AMER: 'American',
PL: 'Place',
UDC: 'Univeristy of the District of Columbia',
AU: 'American University',
AVE: 'Avenue',
CUA: 'Catholic University of America',
NOMA: 'North of Massechusets Avenue',
GMU: 'George Mason University',
VT: 'Virginia Tech',
UVA: 'University of Virginia',
RAEGAN: 'DCA',
ST: 'Street',
SW: 'South West',
SQ: 'Square',
PENN: 'Pennsylvania',
};
function convertStationAcronym(stationName) {
return Object.keys(acronymEnum).reduce((previous, currentKey) => {
const re = new RegExp("\\b" + currentKey + "\\b", "i"); // TODO: escape special characters
if (re.test(stationName)) {
console.log('trigger the match', currentKey)
return previous.concat(' ', acronymEnum[currentKey]);
// ^^^^^^^^
} else {
return previous;
}
}, stationName).toLowerCase();
// ^^^^^^^^^^^
}
console.log(convertStationAcronym('U Street African Amer'))
The alternative solution to the problem can be dividing the input string into an array and for each element in the array, we will find the entry in acronymEnum Object. This approach will be more efficient if the length of the input string is less.
const acronymEnum = {
MT: 'Mountain',
AMER: 'American',
PL: 'Place',
UDC: 'Univeristy of the District of Columbia',
AU: 'American University',
AVE: 'Avenue',
CUA: 'Catholic University of America',
NOMA: 'North of Massechusets Avenue',
GMU: 'George Mason University',
VT: 'Virginia Tech',
UVA: 'University of Virginia',
RAEGAN: 'DCA',
ST: 'Street',
SW: 'South West',
SQ: 'Square',
PENN: 'Pennsylvania',
};
function convertStationAcronym(stationName) {
let stationNameArray = stationName.split(" ");
let result = stationNameArray.map((item)=> {
return acronymEnum[item.toUpperCase()]
? acronymEnum[item.toUpperCase()]
:item
})
return result.join(" ");
}
console.log(convertStationAcronym('U Street African Amer'))
I have an array of zip codes which looks like this:
zipcode_primary = [
{
name: 'New York Location 1',
zip_code: 10001
},
{
name: 'New York Location 2',
zip_code: 10005
},
{
name: 'New York Location 3',
zip_code: 10020
}
]
I am trying to put those locations on a google map as markers. But to my understanding I would have to retrieve the geo coordinates (latitude and longitudes) in order to place markers on the map. So probably the array would look something like this?
zipcode_primary_updated = [
{
name: 'New York Location 1',
zip_code: 10001,
xcord: -73.9,
ycord: 40.7
},
{
name: 'New York Location 2',
zip_code: 10005,
xcord: -74.0,
ycord: 40.7
},
{
name: 'New York Location 3',
zip_code: 10020,
xcord: -73.9800,
ycord: 40.7594
}
]
My question would be how would I find the geo coordinates in reference to the zip codes and store them in a new array.
I have looked into some previously asked questions but didn't really find something that I could use. I found out that it can be done.