Alright, so I have a package react-country-region-selector that provides me with an array CountryRegionData, when I console log it, it's an array of 248 countries,
As you can see in the image for Italy for example, 0 is country name, 1 is country code, 2 is an string of cities seperated by an | and their codes seperated by an ~.
what I would like to do is map this array into a new one, where for each entry it's reformatted to have 3 properties,
country_name using the value in 0
country_code using the value in 1
cities containing an array which has a sub-array for each city that has city_name using the value before the ~ and city_code containing the value after the ~.
I understand this is a bit overwhelming but I'm hoping it would be possible to do using a map function.
Thank you.
You can get the desired structure by mapping over the array itself and mapping over cities of every country inside the arary:
const CountryRegionData = [['Andorra', 'AD', 'Andorra la Vella~07|Canillo~02'], ['Angola', 'AO', 'Bengo~BGO|Benguela~BGU']];
const result = CountryRegionData.map(([country_name, country_code, cities]) => ({
country_name,
country_code,
cities: cities
.split('|')
.map(cityData => cityData.split('~'))
.map(([city_name, city_code]) => ({ city_name, city_code }))
}));
console.log(result)
Here is a destructing version that creates an object array as I assume you meant
let res = [
["Italy", "IT", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy2", "IT2", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy3", "IT3", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy4", "IT4", "Abruzzo~65|Basilicata~77|Calabria~89"],
].map(item => {
const [country_code, country_name, ...rest] = item;
return {country_code, country_name, cities :
rest.map(item => {
return item.split("|").map(city => {
const [city_name, city_code] = city.split("~")
return {city_name, city_code}
})
}).flat()
}
});
console.log(res)
The quick and dirty version...
const rawCountries = [['Italy', 'IT', 'Abruzzo~65|Basilicata~77']];
const countries = rawCountries.map(rawCountry => ({
country_name: rawCountry[0],
country_code: rawCountry[1],
cities: rawCountry[2].split('|').map(rawCity => {
const cityTuple = rawCity.split('~');
return { city_name: cityTuple[0], city_code: cityTuple[1] };
})
}));
console.log(countries);
Should be more maintainable over time...
const rawCountries = [['Italy', 'IT', 'Abruzzo~65|Basilicata~77']];
const parseCountries = (() => {
return (rawCountries) => rawCountries.map(parseCountry);
function parseCountry(rawCountry) {
return {
country_name: rawCountry[0],
country_code: rawCountry[1],
cities: parseCities(rawCountry[2])
};
}
function parseCities(rawCities) {
return rawCities.split('|').map(parseCity);
}
function parseCity(rawCity) {
const countryCodeTuple = rawCity.split('~');
return {
city_name: countryCodeTuple[0],
city_code: countryCodeTuple[1]
};
}
})();
console.log(parseCountries(rawCountries));
Here is a clean and simple solution to this:-
function formatCountries(countryArray) {
var result = [];
countryArray.forEach(country => {
result.push({ country_name: country[0], country_code: country[1], cities: getCities(country[2]) });
});
return result;
}
function getCities(cityStr) {
const cityAndCodeArr = cityStr.split('|');
const result = [];
cityAndCodeArr.forEach(item => {
const tempArr = item.split('~');
result.push({ city_name: tempArr[0], city_code: tempArr[1]});
});
return result;
}
// call the formatCountries function like following
formatCountries(data);
You can use a combination of array reduce and array map to create a new country array
let originalArray = <Your array>
let newCountryArray = originalArray.reduce((newArray, currentElement) => {
let newObj = {};
newObj.country_name = currentElement[0];
newObj.country_code = currentElement[1];
let cities = currentElement[2];
cities = cities.split('|');
cities = cities.map(city => {
city = city.split('~');
return {
city_name: city[0],
city_code: city[1]
};
});
newObj.cities = cities;
newArray.push(newObj);
return newArray;
}, []);
I have made a working snippet for you. You can customize the variables acording to your needs.
let ar = [
["Italy", "IT", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy2", "IT2", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy3", "IT3", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy4", "IT4", "Abruzzo~65|Basilicata~77|Calabria~89"],
]
let newAr = ar.map((x) => {
let cities = x[2].split("|");
let citiesAr = cities.map((y) => {
city = y.split("~");
return {
city_name: city[0],
city_code: city[1]
};
})
return {country_name: x[0], country_code: x[1], cities: citiesAr}
})
console.log(newAr)
let ar = [
["Italy", "IT", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy2", "IT2", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy3", "IT3", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy4", "IT4", "Abruzzo~65|Basilicata~77|Calabria~89"],
]
let newCountryList = [];
ar.reduce((arr, curr) => {
// debugger;
let newContList = {
country_name: curr[0],
country_code: curr[1],
cities: []
}
let newCities = [];
curr[2].split('|').reduce((ar1, ele) => {
let city = ele.split('~');
newCities.push({ city_name: city[0], city_code: city[1] })
}, [])
newContList.cities = newCities;
newCountryList.push(newContList);
}, [])
console.log(newCountryList)
hope this helps...
Yep, the map function.
const CountryRegionData = [
["Italy", "IT", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy2", "IT2", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy3", "IT3", "Abruzzo~65|Basilicata~77|Calabria~89"],
["Italy4", "IT4", "Abruzzo~65|Basilicata~77|Calabria~89"],
];
const mappedCountries = CountryRegionData.map(data => {
const cityArray = data[2]
.split('|')
.map(item => {
const cities = item.split('~');
return {
city_name: cities[0],
city_code: cities[1]
}
});
return {
country_name: data[0],
country_code: data[1],
cities: cityArray
};
});
console.log(mappedCountries);
You can do this way using the map function of the array :
// Data set
const data = [
["Italy", "IT", "Abruzzo~65|AnotherCity~70"],
["Spain", "SP", "City~12|AnotherCity~3"],
]
// Declare the variable that will contain the formatted data
const formattedData = data.map((e) => {
// Split the cities string in order to get an array ["Abruzzo~65",
// "AnotherCity~70]"]
let citiesArray = e[2].split('|')
// Map other this array and split around the "~" to get city name
// and city code in an array ["Abruzzo", "65"]
const cities = citiesArray.map((e) => {
return {
city_name: e.split('~')[0],
city_code: e.split('~')[1]
}
})
return {
country_name: e[0],
country_code: e[1],
cities: cities
}
})
console.log(formattedData)
Related
I am creating searchbar to filter key value data objects. I am getting filter is not a function error while doing search. Is there any other function to filter in key value pair objects ?
Data :-
{
meta_city : {
label: "City"
values: (5) ["DL", "KA", "GJ", "MH", "UP"]
},
meta_country : {
label: "Country"
values: (5) ["IN", "US", "CA"]
}
}
Handle search (filterData data is local state) :-
const handleSearchFilter = (event) => {
const searchWord = event.target.value;
const newFilter = Object.keys(filterData).map((key) => {
filterData[key].filter((value) => {
return value.includes(searchWord);
});
});
setFilterData(newFilter);
};
<div className="mp-input-field-container">
<input
className="mp-input"
placeholder="Search"
onChange={handleSearchFilter}
/>
</div>
You should use reduce like this:
const handleSearchFilter = (event) => {
const searchWord = event.target.value;
const newFilter = Object.keys(filterData).reduce((result, key) => {
if (filterData[key].values.includes(searchWord)) {
result.push(filterData[key]);
};
return result;
}, []);
setFilterData(newFilter);
};
In this example I'm returning an array result. you can return an object if you want.
Filter does not exist on a string type. When filterData[key] is called, key has a value of label. filterData["label"] returns a string "City".
try
const searchWord = (word) => Object.values(filterData).filter((data) => data.values?.includes(word));
const handleSearchFilter = (event) => {
const word = event.target.value;
const [newFilter] = searchWord(word)
setFilterData(newFilter);
}
searchWord returns if you search "DL"
[
{
label: 'City',
values: [ 'DL', 'KA', 'GJ', 'MH', 'UP' ]
}
]
Was that the result you were looking for?
Here is the code snippet to prove the solution works:
var filterData = {
meta_city : {
label: "City",
values: ["DL", "KA", "GJ", "MH", "UP"]
},
meta_country : {
label: "Country",
values: ["IN", "US", "CA"]
}
}
const searchWord = (word) => Object.values(filterData).filter((data) => data.values.includes(word));
console.log(searchWord("DL"))
I'm trying to filter a large array of objects that also have nested values.
I need to match shortName OR description OR isoCode. Some of the items may have 20+ countries but most have 1 to 5.
{
countries: Array(1)
0:
description: "United Kingdom"
isoCode: "GB"
1:
description: "Italy"
isoCode: "IT"
shortName: "AIB (NI)"
},
// * 2000
I've tried building on this with limited success.
methods: {
filterInstitutions: function (items: any, event: any): void {
console.log(items, event.target.value);
if (event === '') {
newFunction(items);
} else {
this.listedInstitutions = items.filter((item: any) => {
return item.shortName.toLowerCase().includes(event.target.value.toLowerCase());
})
}
},
},
I am building this in Vue (typescript) but understand its as much of a JS question than a Vue one.
Any suggestions welcome.
You will need to add in tests for the description and isoCode of your countries property to your filter function.
One way would be to use the Array's some() method which will test if any of the array element's match any test you setup in the callback. It will then return true if any do match and false if not.
let testValue = event.target.value.toLowerCase();
this.listedInstitutions = items.filter((item: any) => {
//test the shortName
let matchesShortName = item.shortName.toLowerCase().includes(testValue);
//loop through countries and see if any description or isoCode match
let matchesDescIsoCode = item.countries.some((item:any) => {
let desc = item.description.toLowerCase();
let code = item.isoCode.toLowerCase();
return desc.includes(testValue) || code.includes(testValue);
});
return matchesShortName || matchesDescIsoCode;
})
Example
function doFilter(event, items) {
let testValue = event.target.value.toLowerCase();
let listedInstitutions = items.filter((item) => {
let matchesShortName = item.shortName.toLowerCase().includes(testValue);
let matchesDescIsoCode = item.countries.some((item) => {
let desc = item.description.toLowerCase();
let code = item.isoCode.toLowerCase();
return desc.includes(testValue) || code.includes(testValue);
});
return matchesShortName || matchesDescIsoCode;
});
console.clear();
console.log("Filtered List");
console.log(listedInstitutions);
}
let someItems = [{
countries: [{
description: "United Kingdom",
isoCode: "GB"
},
{
description: "Italy",
isoCode: "IT"
}
],
shortName: "AIB (NI)"
}, {
countries: [{
description: "United States",
isoCode: "US"
},
{
description: "Italy",
isoCode: "IT"
}
],
shortName: "ABC (DE)"
}]
<input type="text" oninput="doFilter(event,someItems)">
You could create an array of properties to search on within your object(s) and determine if any matches exist.
this.listedInstitutions = items.filter((item: any) => {
return item.countries.filter(
country => ['description', 'isoCode', 'shortName'].filter(prop => item.prop && item.prop.toLowerCase().includes(event.target.value.toLowerCase()).length > 0
).length > 0
})
Using filter() and some():
function filterInstitutions (items, {target: {value}}) {
const isMatch = text => text.toLowerCase().includes(value.toLowerCase());
return items.filter(({shortName, countries}) => (
isMatch(shortName) || countries.some(({description, isoCode}) => (
isMatch(description) || isMatch(isoCode)
))
));
};
I have a json with "proceds" and "teams".
Id´like to transform it grouping it by team.
Next, I show a snippet what I want achieve.
How can I do it?
dados:{
proceds:[
{id:'1', proc:'11', teams:[{team_id:1},{team_id:2}]},
{id:'2', proc:'12', teams:[{team_id:1},{team_id:3}]},
{id:'3', proc:'13', teams:[{team_id:2},{team_id:3}]},
]
}
I´d like to transform "dados" to:
dados:{
teams:[
{team_id:1, proceds:[{id:'1', proc:'11'},{id:'2', proc:'12'}],
{team_id:2, proceds:[{id:'1', proc:'11'},{id:'3', proc:'13'}]
{team_id:3, proceds:[{id:'2', proc:'12'},{id:'3', proc:'13'}]
]
}
You can create an object lookup for each team id and then generate the result using Object.entries and array#map.
const data = [ {id:'1', proc:'11', teams:[{team_id:1},{team_id:2}]}, {id:'2', proc:'12', teams:[{team_id:1},{team_id:3}]}, {id:'3', proc:'13', teams:[{team_id:2},{team_id:3}]}, ],
result = Object.entries(data.reduce((r, o) => {
o.teams.forEach(({ team_id }) => {
r[team_id] = r[team_id] || [];
r[team_id].push({ id: o.id, proc: o.proc });
});
return r;
}, {})).map(([team_id, proceds]) => ({ team_id, proceds }));
console.log(result);
.as-console-wrapper{min-height:100% !important; top: 0; }
You need to iterate each object to make groups.
let dados = {
proceds:[
{id:'1', proc:'11', teams:[{team_id:1},{team_id:2}]},
{id:'2', proc:'12', teams:[{team_id:1},{team_id:3}]},
{id:'3', proc:'13', teams:[{team_id:2},{team_id:3}]},
]
};
const groupByTeams = [];
dados.proceds.forEach(item=> {
let {teams, ...teamItems} = item;
teams.forEach(team=>{
groupByTeams.find(x=>x.team_id==team.team_id)?groupByTeams.find(x=>x.team_id==team.team_id).proceds.push({...teamItems}):groupByTeams.push({...team, proceds: [{...teamItems}]});
});
});
console.log({dados:{teams:groupByTeams}});
You can combine reduce with Object.keys
const asObject = dados.proceds.reduce((acc, curr) => {
curr.teams.forEach(t => acc[t.team_id] = [...(acc[t.team_id] || []), { id: curr.id, proc: curr.proc }])
return acc
}, {})
const result = {
teams: Object.keys(asObject).map(k => ({ team_id: k, proceds: asObject[k] }))
}
const dados = {
proceds:[
{id:'1', proc:'11', teams:[{team_id:1},{team_id:2}]},
{id:'2', proc:'12', teams:[{team_id:1},{team_id:3}]},
{id:'3', proc:'13', teams:[{team_id:2},{team_id:3}]},
]
}
function transform(object) {
object.teams = object.proceds.map((obj) => ({
team_id: parseInt(obj.id),
proceds: [
...obj.teams.map(team => ({
id: team.team_id.toString(),
proc: object.proceds.find(proc => proc.id == team.team_id).proc
}))
]
}))
delete object.proceds;
return object;
}
console.log(transform(dados))
I have data array object like this:
const data = [
{Name: "dian", Job: "programmer"},
{Name: "dian", Job: "gamer"},
{Name: "candra", Job: "programmer"},
]
My goal is to create new data where a have same value join b.
Example output:
const new_data = [
{Name: "dian", Jobs: [{Job: "programmer"}, {Job: "gamer"}]},
{Name: "candra", Jobs: [{Job: "programmer"}]},
]
I think you can use Array.prototype.reduce() to achive your goal. From the documentation:
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
One possible solution:
const data = [
{Name:'dian', Job:'programer'},
{Name:'dian', Job:'gamers'},
{Name:'candra', Job:'programer'}
];
const result = data.reduce((a, current) => {
const found = a.find(f => f.Name === current.Name);
if (found) {
found.Jobs.push({Job: current.Job});
} else {
a.push({
Name: current.Name,
Jobs: [
{ Job: current.Job }
]
});
}
return a;
}, []);
console.log(result);
I hope that helps!
use reduce.
const data = [
{ Name: "dian", Job: "programer" },
{ Name: "dian", Job: "gamers" },
{ Name: "candra", Job: "programer" }
];
const output = Object.values(data.reduce((a, { Name, Job }, i) => {
if (!a[Name]) {
a[Name] = { Name, Jobs: [] };
}
a[Name].Jobs.push({ Job });
return a;
}, {}));
console.log(output);
Here's one approach:
const combineJobs = (data) =>
Object .values (data .reduce (
(a, {Name, Job}, _, __, curr = a [Name] || {Name, jobs: []}) =>
({... a, [Name]: ({... curr, jobs: [... curr .jobs, {Job}]})}),
{}
))
const data = [{Name: "dian", Job: "programmer"}, {Name: "dian", Job: "gamer"}, {Name: "candra", Job: "programmer"}]
console .log (combineJobs (data))
We simply fold our objects into a structure that looks like
{
dian: {Name: 'dian', jobs: [Job:'programer'}, {Job: 'gamer'}]},
candra: {Name: 'candra', jobs: [Job:'programer'}]},
}
then use Object .values to turn it into an appropriate array.
One advantage of this technique is that if your data actually has additional fields not displayed in the question (imagine you have age, and avatar properties as well, for instance), you can extend it easily using a rest parameter:
const combineJobs = (data) =>
Object .values (data .reduce (
(a, {Name, Job, ...rest}, _, __, curr = a [Name] || {Name, jobs: [], ...rest}) =>
// ^^^^^^^ ^^^^^^^
({... a, [Name]: ({... curr, jobs: [... curr .jobs, {Job}]})}),
{}
))
and all those additional parameters would be included.
function modifyArray(data) {
function getNewObject(data) {
const newObject = {
Name: data.Name
};
Object.keys(data).filter(key => key !== 'Name').forEach(key => {
newObject[key+'s'] = [];
newObject[key+'s'].push({
[key]: data[key]
});
});
return newObject;
}
function appendData(obj, data) {
Object.keys(data).filter(key => key !== 'Name').forEach(key => {
obj[key+'s'].push({
[key]: data[key]
});
});
}
const reqArray = [];
data.forEach(d => {
const objToModify = reqArray.find(a => a.Name === d.Name);
if (!objToModify) {
reqArray.push(getNewObject(d));
} else {
appendData(objToModify, d);
}
});
return reqArray;
}
let data =[
{Name:'dian', Job:'programer' },
{Name:'dian', Job:'gamers' },
{Name:'candra', Job:'programer' }
];
console.log(modifyArray(data));
I have an object Regions{} which stores multiple objects, following code block showing countryName : [regions,..,..]
Regions = { Afghanistan:["Badakhshan~BDS", "Badghis~BDG", "Baghlan~BGL"]
Albania:["Berat~01", "Dibër~09", "Durrës~02",]
}
Which giving me result like this:
Afghanistan: Array(n)
0: "Badakhshan~BDS"
1: "Badghis~BDG"
what I am trying to achive is :
Afghanistan: Array(n)
0:{value: "Badakhshan", lable: "BDS"}
1:{value: "Badghis", lable: "BDG"}
thanks in advance
PS: for the sake of some ruthless fellows following is the code what I have tried yet
let countries = CountryRegionData
let regions = {}
countries = countries.map(country => {
regions = {
...regions,
[country[0]]: country[2].split('|')
}
return {
value: country[1],
label: country[0]
}
})
console.log("countries",countries)
console.log("regions",regions)
let values = regions["Afghanistan"];
values = values.map(value =>{
return {
value: value,
lable: value
}
})
You can use split and map, this code is changing values in original object, if you want to build a new object you can use reduce instead of forEach
let Regions = {
Afghanistan: ["Badakhshan~BDS", "Badghis~BDG", "Baghlan~BGL"],
Albania: ["Berat~01", "Dibër~09", "Durrës~02", ]
}
Object.entries(Regions).forEach(([key,value])=>{
Regions[key] = value.map(data=>{
let [value,label] = data.split('~')
return {value,label}
})
})
console.log(Regions)
Do something like:
Regions.map(region => region.map(txt => {
const [val, lbl] = txt.split("~");
return { value: val, lable: lbl};
}));
Messy but gets the work done. Using nested forEach loops
var Regions = {
Afghanistan: ["Badakhshan~BDS", "Badghis~BDG", "Baghlan~BGL"],
Albania: ["Berat~01", "Dibër~09", "Durrës~02", ]
}
var ar = [];
Object.keys(Regions).forEach(function(e) {
Regions[e].forEach(function(k) {
var arr = k.split('~');
ar.push({
value: arr[0],
label: arr[1]
})
})
Regions[e] = ar;
ar = [];
})
console.log(Regions)
Use the map function to iterate the object.
Regions = {
Afghanistan: ["Badakhshan~BDS", "Badghis~BDG", "Baghlan~BGL"],
Albania: ["Berat~01", "Dibër~09", "Durrës~02", ]
};
const finalObject = Object.keys(Regions).map(region => {
return {
[region]: Regions[region].map(country => {
const [value, lable] = country.split("~");
return {
value,
lable
};
})
};
});
console.log(finalObject);