create new city state object - javascript

I would like to transform my initial data to desired result, I am struggling with pushing the cities to the array and making sure name key is unique.
initial data
[
{ "city": "Abbeville", "state": "Louisiana" },
{ "city": "Aberdeen", "state": "Maryland" },
{ "city": "Aberdeen", "state": "Mississippi" },
{ "city": "Aberdeen", "state": "South Dakota" },
{ "city": "Aberdeen", "state": "Washington" },
{ "city": "Abilene", "state": "Texas" },
{ "city": "Abilene", "state": "Kansas" },
{ "city": "Abingdon", "state": "Virginia" },
{ "city": "Abington", "state": "Massachusetts" },
{ "city": "Abington", "state": "Massachusetts" },
]
code
let newCityStateObject = cities.map((item, index) => {
console.log("item ", item);
if (item) {
let object = {};
let citiesArray = [];
//set state and create empty array
if (object[item.state] === undefined) {
object.name = item.state;
object.cities = [].push(item.city);
} else {
//key exists so just push to array
if (object[item.state]) {
object.cities.push(item.city);
}
}
console.log("end ", object);
return object;
}
});
my result right now
[
{ state: 'Louisiana', cities: [] },
{ state: 'Maryland', cities: [] },
{ state: 'Mississippi', cities: [] },
{ state: 'South Dakota', cities: [] },
{ state: 'Washington', cities: [] },
{ state: 'Texas', cities: [] },
{ state: 'Kansas', cities: [] },
{ state: 'Virginia', cities: [] },
]
desired result
[
{
"name": "Kentucky",
"cities": [
"Louisville/Jefferson County",
"Lexington-Fayette",
"Bowling Green",
"Owensboro",
"Covington"
]
},
{
"name": "Maryland",
"cities": [
"Baltimore",
"Frederick",
"Rockville",
"Gaithersburg",
"Bowie",
"Hagerstown",
"Annapolis"
]
}
]
Any help/tips or pointing in the right direction to solve this would be greatly appreciated.

You’re basically grouping cities by their state. First of all, array.map is not the proper method for this problem, cus when you group, the input item’s number might not match with the output’s. array.reduce is better option.
let newCityStateObject = cities.reduce((acc, item, index) => {
if (item) {
// this object has state as key, and the desired output array’s item as value
const object = acc.obj;
// if state not found, create new record
if (object[item.state] === undefined) {
const record = { name: item.state, cities: [] }
object[item.state] = record;
acc.array.push(record);
}
const record = object[item.state];
record.cities.push(item.city);
}
return acc;
}, { obj: {}, array: [] }).array;

I like the answer suggested by #hackape.
Here is one more way to consider:
let newCityStateObject = () => {
const statesArr = Array.from(cities, (item) => item.state);
// Remove state duplications
const filteredStates = [...new Set(statesArr)];
// Initializing the name and cities object and array
let result = filteredStates.map((item) => {
return { name: item, cities: [] };
});
// For each cite item, fetch the city to its corresponding result by state
cities.forEach((item) =>
result
.find((element) => element.name === item.state)
.cities.push(item.city)
);
return result;
}

Related

json API response code using flatmap not working javascript

Problem
I’m trying to return only the objects that don’t contain any of the names on the filteredEmployers list, against the employer attribute, from an API I'm retrieving the data from.
What I've tried
I have an alternative piece of code that seems to work fine when I don’t connect to the API (i.e. reading from hardcoded data), but when I connect to the API, even though I get the following response (when immediately logged after retrieval), the code then doesn’t execute…
{
"Pagination": {
"NumberOfPeople": 185,
"PageSize": 200,
"PageNumber": 1,
"NumberOfPages": 1
},
"People": [
{
"name": "TJ",
"job": "Software Engineer",
"organization": {
"company": {
"employer": "amazon",
"department": "IT"
}
},
"location": {
"city": "Boston",
"state": "Massachusetts"
}
},
{
"name": "Dominique",
"job": "CEO",
"organization": {
"company": {
"employer": "IBM",
"department": "IT"
}
},
"city": "Seattle",
"state": "Washington"
},
{
"name": "Enrique",
"job": "Engineer",
"organization": {
"company": {
"employer": "Bellkrieg Megasystems",
"department": "Construction"
}
},
"location": {
"address": {
"state": "New York",
"city": "New York City",
"zip": "11323"
}
}
},
{
"name": "Bob",
"job": "Project Manager",
"organization": {
"company": {
"employer": "Megasystems",
"department": "R&D"
}
},
"address": {
"location": {
"quadrant": {
"block": 1,
"state": "Texas",
"city": "Austin"
}
}
}
}
]
}
The code I’m trying to implement is here:
// constants and variables are defined here, including API credentials and the filteredEmployers array
//FYI const filteredEmployers = ['Megasystems', 'Bellkrieg'];
//the code then fetches the API data is here
.then((response) => {
return response.json();
})
.then((json) => {
//console.log(typeof json);
//console.log(json);
const people = Array.from(json).flatMap(o => o.People);
return people.filter(person => {
const employer = person?.organization?.company?.employer;
if (typeof employer !== 'string') return true;
const employerIsNotFiltered = filteredEmployers.every(
str => !employer.includes(str)
);
console.log("This is the outputted data: " + employerIsNotFiltered);
return employerIsNotFiltered;
});
})
The desired response is:
[
{
name: 'TJ',
job: 'Software Engineer',
organization: { company: [Object] },
location: { city: 'Boston', state: 'Massachusetts' }
},
{
name: 'Dominique',
job: 'CEO',
organization: { company: [Object] },
city: 'Seattle',
state: 'Washington'
}
]
Any recommendations on how to get this to execute, or alternatives to this method appreciated.
Thanks in advance
Reiterating my comment on your question: You just need to change the line
const people = Array.from(json).flatMap(o => o.People);
to
const people = json.People;
The JSON response that you included in the question is an object, and Response.json() returns a promise which resolves to an already parsed representation of the JSON text response, so in order to access the array at the People property, you only need json.People. Here is a runnable snippet based on the code and data that you showed:
// The JSON data, copied and pasted from the first code block of your question:
const json = `{"Pagination":{"NumberOfPeople":185,"PageSize":200,"PageNumber":1,"NumberOfPages":1},"People":[{"name":"TJ","job":"Software Engineer","organization":{"company":{"employer":"amazon","department":"IT"}},"location":{"city":"Boston","state":"Massachusetts"}},{"name":"Dominique","job":"CEO","organization":{"company":{"employer":"IBM","department":"IT"}},"city":"Seattle","state":"Washington"},{"name":"Enrique","job":"Engineer","organization":{"company":{"employer":"Bellkrieg Megasystems","department":"Construction"}},"location":{"address":{"state":"New York","city":"New York City","zip":"11323"}}},{"name":"Bob","job":"Project Manager","organization":{"company":{"employer":"Megasystems","department":"R&D"}},"address":{"location":{"quadrant":{"block":1,"state":"Texas","city":"Austin"}}}}]}`;
function mockFetch () {
return Promise.resolve({
json: () => Promise.resolve(JSON.parse(json)),
});
}
const filteredEmployers = ['Megasystems', 'Bellkrieg'];
mockFetch()
.then(response => response.json())
.then(json => {
// Change this line:
// const people = Array.from(json).flatMap(o => o.People);
// To:
const people = json.People;
return people.filter(person => {
const employer = person?.organization?.company?.employer;
if (typeof employer !== 'string') return true;
const employerIsNotFiltered = filteredEmployers.every(
str => !employer.includes(str)
);
return employerIsNotFiltered;
});
})
.then(console.log);

How to filter an array of objects (with arrays inside) with another array of objects

I have these two arrays:
filters:
{
location: ['Swiss cottage','Fulham'],
age: ['Giga'],
}
and
data:
[
{
"location": "Swiss cottage",
"ages": "Giga",
},
{
"location": "Fulham",
"ages": "Kilo",
},
{
"location": "Putney",
"ages": "Micro",
}
]
I want to use the first one to filter the second, how should I do this?
You can do something like this. If it's either one of the filters, change && to ||.
let filters = {
location: ['Swiss cottage','Fulham'],
age: ['Giga'],
}
let data= [
{
"location": "Swiss cottage",
"ages": "Giga",
},
{
"location": "Fulham",
"ages": "Kilo",
},
{
"location": "Putney",
"ages": "Micro",
}
]
let x = data.filter(d => {
if (filters.location.includes(d.location) && filters.age.includes(d.ages))
return d;
});
console.log(x);
const filters = {
location: ['Swiss cottage','Fulham'],
age: ['Giga'],
};
const data = [
{
"location": "Swiss cottage",
"ages": "Giga",
},
{
"location": "Fulham",
"ages": "Kilo",
},
{
"location": "Putney",
"ages": "Micro",
}
];
const output = data.filter(item => {
return filters.location.indexOf(item.location) > -1 && filters.age.indexOf(item.ages) > -1;
});
console.log(output)
You could get the entries of filters for having an iterable data set and filter data by checking every key of the entries with the value.
As result you get only the object who match for all properties of the filter.
This approach requires the same property names for filter and data.
AND approach with Array#every
All searched properties must match.
var filters = { location: ['Swiss cottage', 'Fulham'], ages: ['Giga'] },
data = [{ location: "Swiss cottage", ages: "Giga" }, { location: "Fulham", ages: "Kilo" }, { location: "Putney", ages: "Micro" }],
entries = Object.entries(filters),
result = data.filter(o => entries.every(([k, v]) => v.includes(o[k])));
console.log(result);
OR approach with Array#some
One searched property must match.
var filters = { location: ['Swiss cottage', 'Fulham'], ages: ['Giga'] },
data = [{ location: "Swiss cottage", ages: "Giga" }, { location: "Fulham", ages: "Kilo" }, { location: "Putney", ages: "Micro" }],
entries = Object.entries(filters),
result = data.filter(o => entries.some(([k, v]) => v.includes(o[k])));
console.log(result);
It's simple, the arrays in the "filters" object need to include the corresponding values.
let filteredData = data.filter((x) =>
filters.location.includes(x.location) &&
filters.age.includes(x.ages));

Typescript filter an array of objects

I want to filter my results array based on the values stored in filters.
I have tried the below code. But it is not applying.
let filters = {
name: ["Krishna", "Naveen"],
city : ["London"]
};
results = [
{
"name": "Krishna#Surname",
"city": "London",
"age": 23
},
{
"name": "Naveen#Surname",
"city": "London",
"age": 23
},
{
"name": "Krishna#Surname",
"city": "NewYork",
"age": 23
},
{
"name": "Praveen#Surname",
"city": "Washington",
"age": 23
}
]
this.results1 = this.multiFilter(results,filters);
multiFilter(array:any=[], filters:Object) {
const filterKeys = Object.keys(filters);
return array.filter((item) => {
return filterKeys.every(key => {
let filters1= filters[key];
return filters1.every(key1 => {
return !!~ item[key].indexOf(key1)
});
});
});
}
You could check for every filter and returns that object which matches all conditions.
This approach works for an arbitrary count of conditions.
function multiFilter(array, filters) {
return array.filter(o =>
Object.keys(filters).every(k =>
[].concat(filters[k]).some(v => o[k].includes(v))));
}
var filters = { name: ["Krishna", "Naveen"], city: ["London"] },
results = [{ name: "Krishna#Surname", city: "London", age: 23 }, { name: "Naveen#Surname", city: "London", age: 23 }, { name: "Krishna#Surname", city: "NewYork", age: 23 }, { name: "Praveen#Surname", city: "Washington", age: 23 }],
filtered = multiFilter(results, filters);
console.log(filtered);
.as-console-wrapper { max-height: 100% !important; top: 0; }
An alternative is using the function filter along with functions includes and some.
let filters = { name: ["Krishna", "Naveen"], city : ["London"]},
results = [{ "name": "Krishna#Surname", "city": "London", "age": 23},{ "name": "Naveen", "city": "London", "age": 23},{ "name": "Krishna", "city": "NewYork", "age": 23},{ "name": "Praveen", "city": "Washington", "age": 23}],
result = results.filter(({name, city}) => filters.name.some(n => name.includes(n)) && filters.city.includes(city));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can filter the array and use includes to check if name and city is on the array
let filters = {name: ["Krishna", "Naveen"],city: ["London"]};
let results = [{"name": "Krishna","city": "London","age": 23},{"name": "Naveen","city": "London","age": 23},{"name": "Krishna","city": "NewYork","age": 23},{"name": "Praveen","city": "Washington","age": 23}]
let result = results.filter(o => filters.name.includes(o.name) && filters.city.includes(o.city));
console.log(result);

Count object property inside array using lodash or vanilla javascript

I have this object with nested arrays/objects:
{
"USA": [
{
"location": "New York",
"municipality": "Manhattan",
},
{
"location": "Texas",
"municipality": "Austin",
}
],
"CANADA": [
{
"location": "Ontario",
"municipality": "no municipality",
}
]
}
I want to use lodash or plain javascript to count how many location are inside the USA and CANADA. How is that possible?
desired result:
USA: 2
CANADA: 1
Just use the array lengths:
var USA = myObj.USA.length;
var Canada = myObj.CANADA.length;
Or, for larger data sets:
var result = {};
Object.keys(myObj)
.forEach(function(key,index) {
result[key] = myObj[key].length;
});
With lodash you could use mapValues:
let result = _.mapValues(data, 'length');
The solution using Array.prototype.reduce() function:
var obj = {
"USA": [ { "location": "New York", "municipality": "Manhattan" }, { "location": "Texas", "municipality": "Austin" } ], "CANADA": [ { "location": "Ontario", "municipality": "no municipality" }]
},
result = Object.keys(obj).reduce(function(r,k){
r[k] = obj[k].length;
return r;
}, {});
console.log(result)

Organise element in a array

i'm searching a smart way to reoganise an array by a element inside it:
In entry i've got:
[{"name": "brevet",
"country": "fr"
},{
"name": "bac",
"country": "fr"
},{
"name": "des",
"country": "ca"
},{
"name": "dep",
"country": "ca"
}{
"name": "other",,
"country": "other"}]
I want to reorganize my array by country to have this in my output:
[{
"name": "fr",
"degrees": [
{
"name": "brevet",
"country": "fr"
},{
"name": "bac",
"country": "fr"
}]
},{
"name": "ca",
"degrees": [{
"name": "des",
"country": "ca"
},{
"name": "dep",
"country": "ca"
}]
},{
"name": "other",
"degrees": [{
"name": "other",
"country": "other"
}]
}]
For this i write a dirty function, but it seems to me there is a better way but i don't see how. If someone can ligth my brain in a better way to do this i'll be helpfull
private organizeDegrees(degrees: Array<SubscriptionFieldInterface>) {
let degreesByCountry = new Array();
let storeIndex = new Array();
degrees.map(degree => {
let index = null;
storeIndex.find((element, idx) => {
if (element === degree.country) {
index = idx;
return true;
}
});
if (index === null) {
index = degreesByCountry.length;
let newEntry = {
'name': degree.country,
'degrees': new Array()
};
storeIndex.push(degree.country);
degreesByCountry.push(newEntry);
}
degreesByCountry[index].degrees.push(degree);
});
return degreesByCountry;
}
thank's
You can group the array and map the object using Object.keys:
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
var grouped = groupBy(array, "country");
var mappedArray = Object.keys(grouped).map(key => ( {name: key, degrees: grouped [key]} ));
And one more way:
arr = [ /* your array */ ];
arr = Object.values(arr.reduce((ac, el) => {
if(!ac[el.country]) ac[el.country] = {"name": el.country, "degrees": []}
ac[el.country].degrees.push(el);
return ac
}, {}))
console.log(arr) // formated
Another solution, which also handles 'id' => '#id' mapping:
const a = [{"name":"brevet","country":"fr"},{"name":"bac","country":"fr"},{"id":73,"name":"des","country":"ca"},{"name":"dep","country":"ca"},{"name":"other","country":"other"}];
const r = [...new Set(a.map(({country}) => country))] // list of unique country names
.map(c => Object.assign({name: c}, // for each country
{degrees: a.filter(x => x.country === c).map(y => Object.keys(y).includes('id') // handle 'id' => '#id' mutation
? {'#id': "/subscription_fields/" + y.id, name: y.name, country: y.country}
: y)
}))
console.log(r)
This is purely ES6, and quite terse, but possibly less readable. Also, it doesn't add the "#id": "/subscription_fields/83", which could be added as a post process:
const groupByKey = (arr, key) => [...arr.reduce((acc, deg) =>
acc.set(deg[key], {name: deg[key], degrees: [ ...(acc.get(deg[key]) || {degrees: []}).degrees, deg]})
, new Map()).values()];
console.log(groupByKey(degrees, 'country'));
You could use a hash table and collect all values in an object. For getting the result array, you need to push the object only once.
var data = [{ name: "brevet", country: "fr" }, { name: "bac", country: "fr" }, { id: 73, name: "des", country: "ca" }, { name: "dep", country: "ca" }, { name: "other", country: "other" }],
result = data.reduce(function (hash) {
return function (r, a) {
if (!hash[a.country]) {
hash[a.country] = { name: a.country, degrees: [] };
r.push(hash[a.country]);
}
hash[a.country].degrees.push({ name: a.name, country: a.country });
return r;
};
}(Object.create(null)), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources