loop and map through objects in array and group by value - javascript

I am trying to group campaigns in my data by the sales person that created created them but keep going around in circles with different JS array methods.
I have filtered my main data set to return those that have the user role of sales person.
I have my campaign data that shows who created the campaigns
I now have to to group them by sales person.
In my mind I saw it as loop/map through each campaign object in campaigns array, check the createdBy name and return a new array of objects that have the results group by createdBy name.
Sounded easy in my head but can't get around it.
I have put together the following:
const filtered = usersData.filter((val) => val.role === 'sales');
// this returns only the sales people out of all the possible user roles in the following format:
active: true
createdAt: "2019-04-11T21:00:00.000Z"
email: "eliana#example.com"
name: "Alexander Tom Jones"
phone: 3044283743
role: "sales"
_id: "5c8a20d32f8fb814b56fa187"
// my campaign data contains the name of the sales person:
budget: 57154564
company: "sales company"
createdAt: "2020-07-30T09:03:51.925Z"
createdBy: "sales user"
creator_id: "5f228c8d58769fc7d556a6fa"
endDate: "2020-11-08T00:00:00.000Z"
isDeleted: false
location: "Helsinki"
name: "sales test campaign"
// with this function I can return all of the campaigns created by a sales person 1 by 1:
const filteredCampaigns = campaignData.filter(
(val) => val.createdBy === 'sales user'
);
I just cant figure out how to "loop" through each sales person.
I have looked at map / reduce and groupby solutions but nothing comes remotely close. Where am I going wrong?

For grouping by a property, you typically want to use Array.prototype.reduce in a pattern like so:
const bySalesPerson = campaignData.reduce((records, record) => {
// get the property to group by, replace with the
// actual property name you want (createdBy?)
const { salesPerson } = record;
// check if it exists in our accumulator object, if not
// assign empty array
if (!(salesPerson in records)) records[salesPerson] = [];
// push current record into the array associated with
// that value of the property, in this case a particular
// sales person
records[salesPerson].push(record);
// return the accumulator object from the callback
return records;
}, {});

You can use this method to group by sales user. Just call this method like this: const grouped = groupBy(campaignData, ['createdBy']);
groupBy(xs, key) {
return xs.reduce((rv, x) => {
const v = x[key];
const el = rv.find((r) => r && r.key === v);
if (el) {
el.values.push(x);
} else {
rv.push({
key: v,
values: [x]
});
}
return rv;
}, []);
}

Assuming your array contains objects, you can use the filter functionality.
const campaignData = [{
budget: 57154564,
company: "sales company",
createdAt: "2020-07-30T09:03:51.925Z",
createdBy: "sales user",
creator_id: "5f228c8d58769fc7d556a6fa",
endDate: "2020-11-08T00:00:00.000Z",
isDeleted: false,
location: "Helsinki",
name: "sales test campaign"
},
{
budget: 10,
company: "sales company",
createdAt: "2020-07-29T09:03:51.925Z",
createdBy: "buy user",
creator_id: "5f228c8d58769fc7d556a6fb",
endDate: "2020-12-08T00:00:00.000Z",
isDeleted: false,
location: "Stockholm",
name: "sales test campaign"
}
];
const filteredCampaigns = campaignData.filter(
function(item) {
return item.createdBy == 'sales user';
}
);
console.log(filteredCampaigns);

Related

JavaScript need a way to clean up JSON.Stringify without modifying a helper function

Working on an assignment. I am using .filter() to find a list of active members. Following the instructions of the assignment the output is very messy. I would like to only display the member names or the member _ids.
This is the helper function and I cannot modify this function. As a rule of the assignment.
const printKata = function (kataNumber, object) {
const detailsElement = document.createElement("details");
main.append(detailsElement);
const summaryElement = document.createElement("summary");
summaryElement.append("KATA " + kataNumber);
detailsElement.append(summaryElement);
const stringifiedObject = JSON.stringify(object);
detailsElement.append(stringifiedObject);
...
Here is an example of the user object
const users = [
{
_id: "5efb83d333dadb00fc5f68e4",
isActive: true,
balance: "$2,967.17",
picture: "http://placehold.it/32x32",
age: 21,
eyeColor: "green",
name: "Gregory Villarreal",
company: "BLUPLANET",
email: "undefined.undefined#bluplanet.name",
phone: "+1 (866) 544-2326",
registered: "Friday, May 24, 2019 3:08 PM",
tags: ["quis", "irure", "consequat", "ut", "occaecat"],
}
...
And here is the simple function to filter the data.
let userActive = users.filter(function (user) {
return user.isActive === true;
});
...
Currently it is displaying all the data from the entire object. I think it would look a lot nicer to only display the name and or id ..
I am very new to all of this. Please be patient with me.
Currently it is displaying all the data from the entire object. I
think it would look a lot nicer to only display the name and or id ..
To do that, Use map just after your filter method, to specify only fields you want from objects and not all of them.
const users = [{
_id: "5efb83d333dadb00fc5f68e4",
isActive: true,
balance: "$2,967.17",
picture: "http://placehold.it/32x32",
age: 21,
eyeColor: "green",
name: "Gregory Villarreal",
company: "BLUPLANET",
email: "undefined.undefined#bluplanet.name",
phone: "+1 (866) 544-2326",
registered: "Friday, May 24, 2019 3:08 PM",
tags: ["quis", "irure", "consequat", "ut", "occaecat"],
}]
let userActive = users.filter(function(user) {
return user.isActive === true;
}).map(({id,name})=>({id,name}));
console.log(userActive)
This function is returning what you are asking from it:
let userActive = users.filter(function (user) {
return user.isActive === true;
});
"Filter the users array and return the user where isActive is true."
The return is the user object (or users if there is more than one) with all the key/value pairs.
As suggested in the previous answer, you can chain the map method at the end and return the values you want.

How do I get information from one json object with the key of another json object?

I need to get the data from an array of json objects using the key of another array of json objects.
Specifically, what I need to do is get the name from the membership using the data stored in local storage (membershipSubscriptions.membership.id)
Basket Data
Data for the items in the basket - this is an array of objects from local storage
var basketContentsObject = jQuery.parseJSON(localStorage.getItem('localBasket'));
var membershipSubscriptions = basketContentsObject.data.membershipSubscriptions;
{
autoRenew: true
discount: 0
expiryDate: "2021-05-03T00:00:00"
id: "3807760AVVTBVCNPQVDKKCGMRBLGLCGRP"
isRenewal: false
membership: {
id: "7ALGBGVBCLVTGKNVNRTJPVKGBSPNGQPMK"
}
price: 47
startDate: "2020-05-04T00:00:00"
total: 47
}
Membership Data
json from the API that contains more information of this item - this id also an array of objects
$.getJSON( cache_url + "memberships.json", function(membership) {
console.log(ms);
});
{
"description": "Patrons - paying £250 annually",
"htmlDescription": "<div id>\r\n\t<h2>Patron £250 - Paying annually</h2>\r\n</div>",
"id": "7ALGBGVBCLVTGKNVNRTJPVKGBSPNGQPMK",
"imageUrl": "",
"name": "Patrons - £250 Yearly",
"thumbnailUrl": "",
"attribute_Type": "Patrons"
}
Here I am mapping the data from the api and join the keys from the object in local storage that has the same id as the current object from the api.
api.map((obj) => ({ ...obj, ...ls.find(({membership: {id}}) => id === obj.id)}))
There are two things you should look up if you don't know about them. Object Deconstruction and the Spread Operator. Next to that we have map and find that might be worth a look.
Here is a full working example:
const ls = [{
autoRenew: true,
discount: 0,
expiryDate: "2021-05-03T00:00:00",
id: "3807760AVVTBVCNPQVDKKCGMRBLGLCGRP",
isRenewal: false,
membership: {
id: "7ALGBGVBCLVTGKNVNRTJPVKGBSPNGQPMK",
},
price: 47,
startDate: "2020-05-04T00:00:00",
total: 47,
}]
const api = [{
attribute_Type: "Patrons",
description: "Patrons",
id: "7ALGBGVBCLVTGKNVNRTJPVKGBSPNGQPMK",
imageUrl: "",
name: "Patrons",
thumbnailUrl: "",
}]
console.log(
api.map((obj) => ({apiID: obj.id, ...obj,
...ls.find(({
membership: {id}
}) => id === obj.id)
}))
)
But you could also do it the other way around of course.
const ls = [{
autoRenew: true,
discount: 0,
expiryDate: "2021-05-03T00:00:00",
id: "3807760AVVTBVCNPQVDKKCGMRBLGLCGRP",
isRenewal: false,
membership: {
id: "7ALGBGVBCLVTGKNVNRTJPVKGBSPNGQPMK",
},
price: 47,
startDate: "2020-05-04T00:00:00",
total: 47,
}]
let api = [{
attribute_Type: "Patrons",
description: "Patrons",
id: "7ALGBGVBCLVTGKNVNRTJPVKGBSPNGQPMK",
imageUrl: "",
name: "Patrons",
thumbnailUrl: "",
}]
console.log(
ls.map((obj) => ({basketID: obj.id, ...obj,
...api.find(({
id
}) => id === obj.membership.id)
}))
)
You need to store the membership ID in a variable and then iterate through the membership data looking for a coincidence in the ID field.
It could look something like:
const member = "3807760AVVTBVCNPQVDKKCGMRBLGLCGRP";
const memberData = membershipData.find(item => item.id === member);
What this will do is iterate the Membership Data array of objects and, for each object in the array, check if the id property matches the member ID you are looking for. If a match is found, the entire object will be returned and will be accessible in the memberData variable. If no match is found, it will return null.
If there may be more than one coincidence and you need all of them, use filter instead of find, in which case memberData will be an array.
Hope this helps.

Comparing two arrays with field in common then pushing to a new array with corresponding grouped fields

general programming problem here.
I have this array called SPACES
[
{
_id: 5e1c4689429a8a0decf16f69,
challengers: [
5dfa24dce9cbc0180fb60226,
5dfa26f46719311869ac1756,
5dfa270c6719311869ac1757
]
},
{
_id: 5e1c4eb9c9461510407d5e81,
challengers: [ 5dfa24dce9cbc0180fb60226, 5dfa26f46719311869ac1756 ],
}
]
And this array called USERS
[
{
_id: 5dfa24dce9cbc0180fb60226,
name: 'Account 1',
email: 'account1#gmail.com',
spaces: [ 5e1c4689429a8a0decf16f69, 5e1c4eb9c9461510407d5e81 ],
},
{
_id: 5dfa26f46719311869ac1756,
name: 'Account 2',
email: 'account2#gmail.com',
spaces: [ 5e1c4689429a8a0decf16f69, 5e1c4eb9c9461510407d5e81 ]
},
{
_id: 5dfa270c6719311869ac1757,
name: 'Account 3',
email: 'account3#gmail.com',
spaces: [ 5e1c4689429a8a0decf16f69 ]
}
]
What I want to do, is go through both, and instead of having the SPACES.challengers array be just IDS, I would like the array to contain each USER object.
So for example, if the USER has an ID that is inside the SPACES.challengers array, then push the user into that array (which will then be the entire object).
SO FAR I have tried this (I am not very good yet):
users.map( ( user ) => {
spaces.map( ( space ) => {
if ( user.spaces.includes( space._id ) ) {
space.challengers.push(user)
}
} );
} );
However, I am not getting inside the IF block. (Even if I did, not sure if it would work OR if this is even how to do it). It feels Odd doing double maps, as I get so many iterations, and it duplicates my push (cause I have no logic to see if it just has been pushed).
Assuming every entry in the Users array has a unique ID, we can build a Hashmap to store (id, index) pairs in order to search efficiently for an ID from Users array while looping through Spaces array.
let spaces = [{_id: '5e1c4689429a8a0decf16f69',challengers: ['5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756', '5dfa270c6719311869ac1757']},{_id: '5e1c4eb9c9461510407d5e81',challengers: [ '5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756' ],}]
let users = [{_id: '5dfa24dce9cbc0180fb60226',name: 'Account 1',email: 'account1#gmail.com',spaces: [ '5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81' ],},{_id: '5dfa26f46719311869ac1756',name: 'Account 2',email: 'account2#gmail.com',spaces: [ '5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81' ]},{_id: '5dfa270c6719311869ac1757',name: 'Account 3',email: 'account3#gmail.com',spaces: [ '5e1c4689429a8a0decf16f69' ]}]
let IDIndexMapping = {} // To store (_id, index) pairs, in order to improve search efficiency
for(let index in users) // Iterate through Users array using index
IDIndexMapping[users[index]._id] = index; // store (_id, index) pair in IDIndexMapping
// I'm avoiding using `map` and using vanilla `for` loop for space efficiency
// as map returns a new array but with `for` loop, we can perform changes in-place
for(let outerIndex in spaces){ // Iterate through `spaces` array using index
let challengers = spaces[outerIndex].challengers; // Get challengers array
for(let innerIndex in challengers){ // Iterate through challengers array using index
let ID = challengers[innerIndex]; // Get ID
if(ID in IDIndexMapping) // If ID exists in IDIndexMapping
spaces[outerIndex].challengers[innerIndex] = users[IDIndexMapping[ID]]; // Change ID to actual User object
}
}
console.log(spaces)
Output
[ { _id: '5e1c4689429a8a0decf16f69',
challengers: [ [Object], [Object], [Object] ] },
{ _id: '5e1c4eb9c9461510407d5e81',
challengers: [ [Object], [Object] ] } ]
.map and .find should work here. keep it simple.
var spaces = [
{
_id: "5e1c4689429a8a0decf16f69",
challengers: [
"5dfa24dce9cbc0180fb60226",
"5dfa26f46719311869ac1756",
"5dfa270c6719311869ac1757"
]
},
{
_id: "5e1c4eb9c9461510407d5e81",
challengers: ["5dfa24dce9cbc0180fb60226", "5dfa26f46719311869ac1756", "some non existent"]
}
],
users = [
{
_id: "5dfa24dce9cbc0180fb60226",
name: "Account 1",
email: "account1#gmail.com",
spaces: ["5e1c4689429a8a0decf16f69", "5e1c4eb9c9461510407d5e81"]
},
{
_id: "5dfa26f46719311869ac1756",
name: "Account 2",
email: "account2#gmail.com",
spaces: ["5e1c4689429a8a0decf16f69", "5e1c4eb9c9461510407d5e81"]
},
{
_id: "5dfa270c6719311869ac1757",
name: "Account 3",
email: "account3#gmail.com",
spaces: ["5e1c4689429a8a0decf16f69"]
}
],
result = spaces.map(({ _id, challengers }) => ({
_id,
challengers: challengers.map(challenger =>
users.find(user => user._id === challenger)
).filter(row => row)
}));
console.log(JSON.stringify(result, null, 2));
You can create a map of challengers for look-up and then put them in spaces.
//create user map for look-up
userMap = users.reduce((res, val) => ({
...res,
[val._id]: val
}), {});
//change challenger id with user object
inflatedSpaces = spaces.map(s => ({ ...s, challengers: s.challengers.map(c => userMap[c]) }));
You could map the users with a Map.
Beside the destructuring of the object for mapping this answer uses for this part
challengers: challengers.map(
Map.prototype.get, // cb with a prototype and using `this`
new Map(users.map(o => [o._id, o])) // thisArg
)
the above mentioned Map in two parts.
The lower part generates an instance of Map where _id of the users items is used as key and the whole object as value. This instance is uses as thisArg of Array#map, the second parameter.
The upper part is a prototype of Map, used as callback. And while an this object is supplied, a binding (Function#bind) is not necessary.
var spaces = [{ _id: '5e1c4689429a8a0decf16f69', challengers: ['5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756', '5dfa270c6719311869ac1757'] }, { _id: '5e1c4eb9c9461510407d5e81', challengers: ['5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756'] }],
users = [{ _id: '5dfa24dce9cbc0180fb60226', name: 'Account 1', email: 'account1#gmail.com', spaces: ['5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81'] }, { _id: '5dfa26f46719311869ac1756', name: 'Account 2', email: 'account2#gmail.com', spaces: ['5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81'] },{ _id: '5dfa270c6719311869ac1757', name: 'Account 3', email: 'account3#gmail.com', spaces: ['5e1c4689429a8a0decf16f69'] }],
result = spaces.map(({ _id, challengers }) => ({
_id,
challengers: challengers.map(
Map.prototype.get,
new Map(users.map(o => [o._id, o]))
)
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Modify array so it will contain data under unique username

I have array that contains plenty of data. Format is always like that:
1:
UserName: "John Smith"
Priority: "2"
Time Occured: "02/09/2019 11:20:23"
Time Ended: "02/09/2019 11:20:23"
2:
UserName: "Tom Bill"
Priority: "4"
Time Occured: "01/08/2019 13:20:23"
Time Ended: "04/08/2019 15:20:23"
3:
UserName: "John Smith"
Priority: "2"
Time Occured: "06/08/2019 13:20:23"
Time Ended: "09/09/2019 15:20:23"
...
Of course there is more stuff, but just to give you idea of structure.
Array contains entries that might be under the same user name. As user can have multiple entries
What I want to do, is sort and modify it to the way I can use it on data table. I am not sure what approach might be the best or what is possible.
I was thinking that I need to modify array do some math in meantime. So in Data table I can present that John Smith, got 8 entries, two of them are sev 4 etc etc. Tom Bill got 4 entries etc. Basically I won't use original data as I need to modify some parts of it, for Example I am not interested in date itself, but if it was in the past or in the future, already got scripts for that, yet I need to do it for every single user.
A structure something like this seems to be sufficient for your requirement:
data = {
'John Smith' : [{ Priority : 1, .... }, { ...2nd instance }],
'John Doe' : [{...1st instance of John Doe}],
}
Basically an object that has the names for keys, and each key has an array of entries of data.
Whenever you wish to add more entries to John Smith, you get access to the array directly by using data['John Smith']
EDIT
To convert the data to this format.
data = [
{
'UserName': "John Smith",
'Priority': "2",
'Time Occured': "02/09/2019 11:20:23",
'Time Ended': "02/09/2019 11:20:23",
},
{
'UserName': "Tom Bill",
'Priority': "4",
'Time Occured': "01/08/2019 13:20:23",
'Time Ended': "04/08/2019 15:20:23",
},
{
'UserName': "John Smith",
'Priority': "2",
'Time Occured': "06/08/2019 13:20:23",
'Time Ended': "09/09/2019 15:20:23",
}
]
convertData = (data) =>{
let newData = {}
for(let i = 0; i<data.length; i++){
// console.log(data[i])
let name = data[i]['UserName']
tempData = {
'Priority' : data[i]['Priority'],
'Time Occured' : data[i]['Time Occured'],
//Add more properties here
}
if (newData[name]==null){
newData[name] = []
}
newData[name] = [...newData[name], tempData]
}
console.log(newData)
}
convertData(data)
Look at this codepen.
https://codepen.io/nabeelmehmood/pen/jONGQmX

How do check for key value pair over multiple arrays and filter with javascript?

I'm trying to filter data by key value pairs. I need to be able to check if a key value pair exists across multiple arrays.
I have some working code to count the number of objects containing a key value pair but run into problems when there are more that one array.
var data = [{
LastName: "Johnson",
Id: 2222,
specialties: [{
Specialty: "Cardiology",
BoardCertified: true,
IsPrimary: true
},
{
Specialty: "Pediatrics",
BoardCertified: true,
IsPrimary: true
}
],
},
{
LastName: "Hamilton",
Id: 2332,
specialties: [{
Specialty: "Pediatrics",
BoardCertified: true,
IsPrimary: true
}, ],
}
]
var specialty = "Pediatrics";
var filtered = data.filter(function(item) {
return item.specialties[0].Specialty == specialty;
});
console.log(filtered.length);
This counts the items with a key value pair of "Specialty: “Pediatrics"".
Right now this will only return "1" but I want it to return "2".
Is this possible?
return item.specialties.Specialty == specialty; //does not work
If you are sure it will only appear once in each grouping, you can modify your filtering function:
var filtered = data.filter(function(item){
return item.specialties.some(item => item.Specialty === specialty);
)};
Or with two arrow functions:
var filtered = data.filter(item =>
item.specialties.some(item =>
item.Specialty === specialty)
)

Categories

Resources