How to Create Arrays of Objects With A Common Prop Value - javascript

Let's say I have an array of objects like:
flattenedObjects = [
{name: 'Bill', city: 1},
{name: 'Guillermo', city: 1},
{name: 'Wilhem', city: 1},
{name: 'William', city: 1},
{name: 'Nick', city: 2},
{name: 'Nicolas', city: 2},
{name: 'Nicholas', city: 2},
{name: 'Rick', city: 3}
]
I want to create individual arrays of objects grouped by "city". In the code, I will also deconstruct each object so that the final output will be:
boston = ['Bill', 'Guillermo', 'Wilhelm', 'William']
miami = ['Nick', 'Nickolas', 'Nicholas']
london = ['Rick']
I am having difficulties creating the grouped array of objects.
I can do it with one single object, as such:
let boston = flattenedObjects.filter(function (obj) {
return obj.city == 1;
});
What I was thinking of doing was to take a iterate through an object and filtering dynamically, like so:
let cities = {
boston: 1,
miami: 2,
london: 3
}
And then trying something like:
let newObj = flattenedObjects.filter(function (x) {
let obj = {};
Object.entries(cities).forEach(([key, value]) => {
obj["name"] = `${key}`;
obj["city"] = x.city == `${value}`;
return obj;
});
});
This isn't consoling what is expected. It's just an array of objects very similar to the "obj" up above.

let flattenedObjects = [
{name: 'Bill',city: 1},
{name: 'Guillermo',city: 1},
{name: 'Wilhem',city: 1},
{name: 'William',city: 1},
{name: 'Nick',city: 2},
{name: 'Nicolas',city: 2},
{name: 'Nicholas',city: 2},
{name: 'Rick',city: 3}
];
let cities = {
boston: 1,
miami: 2,
london: 3
}
let data = {}
for (const [key, value] of Object.entries(cities)) {
data[key] = flattenedObjects.filter(p => p.city === value).map(e => e.name);
}
console.log(data)

Create a reverse map of city code to city name, O(1) constant time lookups.
Reduce the flattenedObjects array into an object using the city name as a key and generate an array fo the names, O(n) linear access.
const flattenedObjects = [
{ name: "Bill", city: 1 },
{ name: "Guillermo", city: 1 },
{ name: "Wilhem", city: 1 },
{ name: "William", city: 1 },
{ name: "Nick", city: 2 },
{ name: "Nicolas", city: 2 },
{ name: "Nicholas", city: 2 },
{ name: "Rick", city: 3 }
];
const cities = {
boston: 1,
miami: 2,
london: 3
};
const citiesByCode = Object.fromEntries(
Object.entries(cities).map(([city, code]) => [code, city])
);
const groupedResult = flattenedObjects.reduce((groups, current) => {
const cityCode = citiesByCode[current.city];
if (!groups[cityCode]) groups[cityCode] = [];
groups[cityCode].push(current.name);
return groups;
}, {});
console.log(groupedResult);

You could use a reduce statement to reduce the flattenedObjects array into a single object in the format that you want.
const flattenedObjects = [
{name: 'Bill', city: 1},
{name: 'Guillermo', city: 1},
{name: 'Wilhem', city: 1},
{name: 'William', city: 1},
{name: 'Nick', city: 2},
{name: 'Nicolas', city: 2},
{name: 'Nicholas', city: 2},
{name: 'Rick', city: 3},
];
// the keys are the city number rather than city name
const cities = {
1: 'boston',
2: 'miami',
3: 'london',
};
const obj = flattenedObjects.reduce((o, flattenedObject) => {
const cityName = cities[flattenedObject.city];
if (o[cityName] === undefined) {
o[cityName] = [];
}
o[cityName].push(flattenedObject.name);
return o;
}, {});
console.log(obj);

Related

How to create object fields dynamically based on values in Javascript?

I have an object array like this:
const values = [{name: 'name1', index: 1}, {name: 'name2', index: 2}, {name: 'name3', index: 3}]
Is it possible to create such an object dynamically?
const result = {
name1: 1,
name2: 2,
name3: 3
}
what you can do is the following!
const values = [{name: 'name1', index: 1}, {name: 'name2', index: 2}, {name: 'name3', index: 3}];
const result = {};
values.forEach(value => result[value.name] = value.index);
console.log(result); // {name1: 1, name2: 2, name3: 3}

Push multiple objects to an already initialized, empty object array

People is my model, data is my new information, and the forEach is how I am trying to insert the new data into my model, but formatted to only the information I care about
people = [{name: '', age: 0}];
data = [
{id: '123', name: 'Bob', lastName: 'Guy', age: 40},
{id: '321', name: 'Michael', lastName: 'Park', age: 20},
]
data.forEach(person => {
people.push({
name: person.name,
age: person.age,
});
});
However, the result I get is this:
people = [
{name: '', age: 0},
{name: 'Bob', age: 40},
{name: 'Michael', age: 20}
];
I'm trying to have the object array look like this instead:
people = [
{name: 'Bob', age: 40},
{name: 'Michael', age: 20}
];
However, I would like to know if theres a way to do it without an extra line of code (like popping the first element), and if theres a way to do it in one command? If not, I am open to suggestions. Thank you!
You're using the original array and not only that but also you're mutating the array.
You can use the function Array.prototype.map in order to generate a new array with the desired data.
const people = [{name: '', age: 0}];
const data = [
{id: '123', name: 'Bob', lastName: 'Guy', age: 40},
{id: '321', name: 'Michael', lastName: 'Park', age: 20},
]
const result = data.map(person => ({
name: person.name,
age: person.age,
}));
console.log(result);
You can also keep the desired keys and by using the functions Array.prototype.map and Array.prototype.reduce you can build the expected result:
const model = ["name", "age"];
const data = [
{id: '123', name: 'Bob', lastName: 'Guy', age: 40},
{id: '321', name: 'Michael', lastName: 'Park', age: 20},
]
const result = data.map(person => model.reduce((r, m) => ({...r, [m]: person[m]}), {}), []);
console.log(result);
Just in case you need to implement different person models, you can dinamically create the objects like this
peopleModel = [{ name: "", age: 0 }];
data = [
{ id: "123", name: "Bob", lastName: "Guy", age: 40 },
{ id: "321", name: "Michael", lastName: "Park", age: 20 },
];
const keysArr = Object.keys(peopleModel[0]);
const totalKeys = keysArr.length;
const people = data.reduce((acc, personObj) => {
const obj = {};
keysArr.forEach((key) => {
if (personObj[key]) {
obj[key] = personObj[key];
}
});
acc.push(obj);
return acc;
}, []);
console.log(people);
/* logs [
{name: 'Bob', age: 40},
{name: 'Michael', age: 20}
];
*/
but if you need a different model like
peopleModel = [{ name: "", age: 0, lastName: "" }]
you will get for the log the following:
[
{name: 'Bob', age: 40, lastName: 'Guy'},
{name: 'Michael', age: 20, lastName: 'Park'}
];
that way you do not need to hardcode the keys

Multiply/Clone multiple times objects inside and array in JavaScript

Having an array of objects in this format:
const data = [
{country_id: 1, country: "Greece", value: 3},
{country_id: 2, country: "Cyprus", value: 2},
{country_id: 3, country: "Turkey", value: 4}
]
how can I multiply/clone each of the objects to get the following array of objects using JavaScript? I want to multiply each object as many times as specified in value and get a new array.
const new_data = [
{id: 1, country_id: 1, country: "Greece", value: 3},
{id: 2, country_id: 1, country: "Greece", value: 3},
{id: 3, country_id: 1, country: "Greece", value: 3},
{id: 4, country_id: 2, country: "Cyprus", value: 2},
{id: 5, country_id: 2, country: "Cyprus", value: 2},
{id: 6, country_id: 3, country: "Turkey", value: 4},
{id: 7, country_id: 3, country: "Turkey", value: 4},
{id: 8, country_id: 3, country: "Turkey", value: 4},
{id: 9, country_id: 3, country: "Turkey", value: 4}
]
My best attempt so far is this one using Object.assign but unfortunately the map returns the same array as in data:
const new_data = data.map((d, i) => {
for (var i = 0; i < d.mult; ++i) {
Object.assign({}, d[i]);
}
return d;
})
You could do it like this, where you simply fill an array with value elements, and map it to a clone of the original element:
const data = [
{country_id: 1, country: "Greece", value: 3},
{country_id: 2, country: "Cyprus", value: 2},
{country_id: 3, country: "Turkey", value: 4}
]
console.log(
data.flatMap((el) => new Array(el.value).fill(null).map(e => ({...el}))))
You can do it using Array.from() and reduce. Try this-
Here you can create a temporary array using Array.from({length: X}). Here the {length: X} object says the from method to create an array with the length of X.
After that, the callback function of the Array.from() method returns the current item for every iteration. That's it.
const data = [
{country_id: 1, country: "Greece", value: 3},
{country_id: 2, country: "Cyprus", value: 2},
{country_id: 3, country: "Turkey", value: 4}
];
const res = data.reduce((acc, curr) => {
const tmp = Array.from({length: curr.value}, () => curr);
acc.push(...tmp);
return acc;
}, []);
console.log(JSON.stringify(res));
.as-console-wrapper{min-height: 100%!important; top: 0}
The easiest would be to just create a new array, loop through the old one and add duplicates (with the new id field) to the result array:
const data = [
{ country_id: 1, country: "Greece", value: 3 },
{ country_id: 2, country: "Cyprus", value: 2 },
{ country_id: 3, country: "Turkey", value: 4 }
]
const result = [];
let id = 0;
for (const row of data) {
for (let i = 0; i < row.value; i++) {
const newRow = {
...row, // copy old data
id: ++id, // but set this field and increase `id`
};
result.push(newRow);
}
}
console.log(result);
If you want to use something like .map, you'd need to use .flatMap or .map followed by .flat(). Your (flat)map would return an array with the value new rows. You'd still have to fix the country_id though.

How do I search and iterate this object for an id?

I have an object where the values are arrays and they've further objects inside them like this:
let primaryStandard = {
section1: [{name: 'andy', id: 1}, {name: 'charles', id: 2},...],
section2: [{name: 'megan', id: 55}, {name: 'derek', id: 56},...],
section3: [{name: 'robert', id: 95}, {name: 'nathan', id: 96},...],
}
Basically, I want to iterate this whole object for a particular id value and then get the name and section in an object.
Example:
For id = 95 the result should be {section: section3, name: 'robert`}
What I've tried so far:
let primaryStandard = {
section1: [{name: 'andy', id: 1}, {name: 'charles', id: 2}],
section2: [{name: 'megan', id: 55}, {name: 'derek', id: 56}],
section3: [{name: 'robert', id: 95}, {name: 'nathan', id: 96}],
}
for (let key of Object.keys(primaryStandard)) {
console.log((primaryStandard[key])) // logs the values(array)
primaryStandard[key].map(student => console.log(student)) // .map() is not defined error
}
You could do this using find method on Object.keys to get section and name.
let data = {section1: [{name: 'andy', id: 1}, {name: 'charles', id: 2}],section2: [{name: 'megan', id: 55}, {name: 'derek', id: 56}],section3: [{name: 'robert', id: 95}, {name: 'nathan', id: 96}],}
let name;
let section = Object.keys(data).find(key => {
const match = data[key].find(({id}) => id == 95);
if(match) return name = match.name
})
console.log({name, section})

Iterating on nested arrays in objects to collate informaton

I have the following array of objects:
let array = [
{key: 'Newcastle', values[
{key: 'ID1000', values[
{name: 'Jeff', cust_id: "ID1000", status: 'sold'},
{name: 'Jeff', cust_id: "ID1000", status: 'pending'},
]}
{key: 'ID2000', values [
{name: 'Bob', cust_id: "ID2000", status: 'sold'}
]}
]}
{key: 'London', values [
{key: 'ID3000', values[
{name: 'Gary', cust_id: "ID3000", status: 'sold'},
]}
{key: 'ID4000', values[
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'pending'},
]}
]}
]
I have been trying to refactor it into something like:
[
{Location: 'Newcastle, customers: 2, sold: 2, pending: 1, interest: 0},
{Location: 'London', customers: 2, sold: 1, pending: 1, interest: 2}
]
So I am attempting to count the number of status events and collate them accordingly.
I get lost when I try to iterate on the nested arrays and then when trying to bubble up the results of the iteration to a final object. The closest I have got is:
function transform(array) {
let arr = []
array.forEach(function(x) {
function soldCount() {
x.values.forEach(function(x) {
let sold = x.values.forEach(function(x) {
let soldTrue = 0
if (x.status === "sold") {
soldTrue++
}
console.log(soldTrue)
if (soldTrue > 0) {
return soldTrue
}
})
})
}
let obj = {
location: x.key,
customers: x.values.length,
sold: soldCount()
}
arr.push(obj)
})
return arr
}
This tries to iterate on each array in the objects and attempts to return a number for how many of the 'sold' status it finds. The console statement does return a number but it returns multiple entries for each item in the array due to 'forEach'.
I am swamped in a number of forEach loops iterating on nested arrays. I suspect that this might not be the correct methodology for what I am trying to achieve.
You could take an object counting status and build a new objects.
var array = [{ key: 'Newcastle', values: [{ key: 'ID1000', values: [{ name: 'Jeff', cust_id: "ID1000", status: 'sold' }, { name: 'Jeff', cust_id: "ID1000", status: 'pending' },] }, { key: 'ID2000', values: [{ name: 'Bob', cust_id: "ID2000", status: 'sold' }] }] }, { key: 'London', values: [{ key: 'ID3000', values: [{ name: 'Gary', cust_id: "ID3000", status: 'sold' },] }, { key: 'ID4000', values: [{ name: 'Mary', cust_id: "ID4000", status: 'interest' }, { name: 'Mary', cust_id: "ID4000", status: 'interest' }, { name: 'Mary', cust_id: "ID4000", status: 'pending' }] }] }],
result = array.map(({ key: Location, values }) => {
var data = { Location, customers: 0, sold: 0, pending: 0, interest: 0 };
values.forEach(({ values }) => {
data.customers++;
values.forEach(({ status }) => data[status]++);
});
return data;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Look at this:
let array = [
{
key: 'Newcastle',
values: [
{key: 'ID1000',
values:[
{name: 'Jeff', cust_id: "ID1000", status: 'sold'},
{name: 'Jeff', cust_id: "ID1000", status: 'pending'},
]},
{
key: 'ID2000',
values :[
{name: 'Bob', cust_id: "ID2000", status: 'sold'}
]
}
]},
{key: 'London', values :[
{key: 'ID3000', values:[
{name: 'Gary', cust_id: "ID3000", status: 'sold'},
]},
{key: 'ID4000', values:[
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'pending'},
]}
]}
]
function countProp(arr, prop) {
let n = 0;
arr.forEach((items) => {
items.values.forEach(item => {
if (item.status === prop) {
n += 1;
}
})
});
return n;
}
const n = array.map(item => {
const a = {};
a.customers = item.values.length;
a.location = item.key;
a.sold = countProp(item.values, "sold");
a.pending = countProp(item.values, "pending");
a.interest = countProp(item.values, "interest");
return a;
});
This gives
[
{Location: 'Newcastle, customers: 2, sold: 2, pending: 1, interest: 0},
{Location: 'London', customers: 2, sold: 1, pending: 1, interest: 2}
]
Assumingly you are using let.
I have written my solution in ES6, getting Location, customer count is easy.
The challenging part would be the accumulating the status count per type of status, you can use reduce with object as initial value and using the status types as keys with 0 initial value, which shown in my solution
let array = [
{key: 'Newcastle', values: [
{key: 'ID1000', values: [
{name: 'Jeff', cust_id: "ID1000", status: 'sold'},
{name: 'Jeff', cust_id: "ID1000", status: 'pending'}
]},
{key: 'ID2000', values: [
{name: 'Bob', cust_id: "ID2000", status: 'sold'}
]}
]},
{key: 'London', values: [
{key: 'ID3000', values: [
{name: 'Gary', cust_id: "ID3000", status: 'sold'}
]},
{key: 'ID4000', values:[
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'interest'},
{name: 'Mary', cust_id: "ID4000", status: 'pending'}
]}
]}
];
const newArray = array.map((a) => {
const Location = a.key;
const customer = a.values.length;
const status = a.values.reduce((acc, value) => {
value.values.forEach((v1) => {
acc[v1.status] = acc[v1.status] + 1;
});
return acc;
}, {sold: 0, pending: 0, interest: 0});
return {Location, customer, ...status};
});
console.log(newArray);

Categories