how can i get all the customers with the same id in each Object into One array (unless you have a better idea)
so i can get the start and end time to show in a table for that specific customer.
the idea here is to show a table with fixed columns, and each row would be filled with customer name and start time for each of these columns instead of having multiple rows.
im looping over my API data in which it's returning multiple Objects with each having its own values for Example:
Better to use Object, where the key is the actual ID identifier: {ID1: [], ID2: []}- that way by just targeting the ID you get back all the data for that customer ID:
const response = [
{customerId:"11", customerName:"John", serviceName: "foo"},
{customerId:"88", customerName:"Anne", serviceName: "bar"},
{customerId:"11", customerName:"John", serviceName: "baz"},
];
const customers = response.reduce((dict, data) => {
if (!dict[data.customerId]) dict[data.customerId] = [];
dict[data.customerId].push(data);
return dict;
}, {});
// Get specific customer data (customerId "11"):
console.log(customers["11"]);
// Get all customers data:
console.log(customers);
This can also be done using the Map Object and its has, get and set methods:
const response = [
{customerId:"11", customerName:"John", serviceName: "foo"},
{customerId:"88", customerName:"Anne", serviceName: "bar"},
{customerId:"11", customerName:"John", serviceName: "baz"},
];
const customers = response.reduce((m, {customerId:id, ...r}) => {
if (!m.has(id)) m.set(id, []);
return (m.get(id).push(r), m);
}, new Map());
// Get specific customer data (customerId "11"):
console.log(customers.get("11"));
// Get all customers data:
console.log([...customers]);
Related
Is there a common known way to chain .map or .filter or .find expressions to accomplish this kind of lookup?
Given and array of objects within an array of objects
customerGroups :
[
{
id: 1,
customers: [{
id: 1, // The same customer may appear in multiple groups
name: 'Jhon'
}],
},
{
id: 2,
customers: [{
id: 2,
name: 'Jhon'
}],
},
{
id: 3,
customers: [{
id: 2,
name: 'Doe'
}],
},
]
In the use case where you have the customer.id and want to find out the customer.name I would like to extract the customers array to use the Array.Find method
const idSearch = 1
const customerName = customers.find(({id})=>id==idSearch).name
So far I been trying with
const customers = customerGroup.find(({ customer }) =>
customer.find(({ id }) =>idSearch === id),
)?.customers
const customerName = customers.find(({id})=>id==idSearch).name
I believe there is a better way to do this but I'm too burnout to figure it out.
I've also tried some shenanigans with the .map to make a new array with all the customers in it but no good results so far.
I could also fetch that array from my Backend but I already have all the customers in memory so that would be an overheat.
There is not one native method that does this, but you could first combine the customer arrays into one with flatMap, and then use find:
const customerGroups = [{id:1,customers:[{id:1,name:'Jhon'}]},{id:2,customers:[{id:2,name:'Jhon'}]},{id:3,customers:[{id:2,name: 'Doe'}]}];
const idSearch = 1;
const allCustomers = customerGroups.flatMap(({customers}) => customers);
const name = allCustomers.find(({id}) => id === idSearch)?.name;
console.log(name);
This approach works because as soon as the inside find loop discovers a result, both the inside and outside loop will terminate, leaving name set as the match which caused the loops to terminate (or as undefined if no match was found).
const d = [{id:1,customers:[{id:1,name:'Jhon'}]},{id:2,customers:[{id:2,name:'Jhon'}]},{id:3,customers:[{id:3,name: 'Doe'}]}]
const idSearch = 1
let name
d.find(j=>j.customers.find(i=>i.id===idSearch && ({name}=i)))
console.log(name)
My current logic is the following creating adding a document with a user id in to users collection, and push the first object, then on the second call I want to be able to update the excising array with a new object.
const docRef = doc(db, 'user', _authContext.currentUser.uid);
const payload = { items: [{item1 : 1}] };
await setDoc(docRef, payload);
// New array after update (second call of the function)
items: [{item1 : 1}, {item2 : 2}]
I think you're looking for:
await updateDoc(docRef, { items: arrayUnion({ item2: 2 }) });
Also see the Firebase documentation on adding items to an array.
I have the following Schema with a array of ObjectIds:
const userSchema = new Schema({
...
article: [{
type: mongoose.Schema.Types.ObjectId,
}],
...
},
I will count the array elements in the example above the result should be 10.
I have tried the following but this doesn't worked for me. The req.query.id is the _id from the user and will filter the specific user with the matching article array.
const userData = User.aggregate(
[
{
$match: {_id: id}
},
{
$project: {article: {$size: '$article'}}
},
]
)
console.log(res.json(userData));
The console.log(article.length) give me currently 0. How can I do this? Is the aggregate function the right choice or is a other way better to count elements of a array?
Not sure why to use aggregate when array of ids is already with user object.
Define articles field as reference:
const {Schema} = mongoose.Schema;
const {Types} = Schema;
const userSchema = new Schema({
...
article: {
type: [Types.ObjectId],
ref: 'Article',
index: true,
},
...
});
// add virtual if You want
userSchema.virtual('articleCount').get(function () {
return this.article.length;
});
and get them using populate:
const user = await User.findById(req.query.id).populate('articles');
console.log(user.article.length);
or simply have array of ids:
const user = await User.findById(req.query.id);
console.log(user.article.length);
make use of virtual field:
const user = await User.findById(req.query.id);
console.log(user.articleCount);
P.S. I use aggregate when I need to do complex post filter logic which in fact is aggregation. Think about it like You have resultset, but You want process resultset on db side to have more specific information which would be ineffective if You would do queries to db inside loop. Like if I need to get users which added specific article by specific day and partition them by hour.
Background of my issue:
To start I had an array of objects each containing a user-ID called entries. I had to make an api call using the id inside each of those object to get the users full profile containing their fullName. I was able to map over the results of the api call to get a list of all users that had matching id's to the initial list I started with and with that I also pulled out their fullName this variable is labeled matches. So now I have the initial list [array] of userID's entries, along with a list of arrays of all the ID matches I found inside that list from my api call matches this list of arrays contains the ID and a Name.
What I am trying to do now is map over the entries and matches to compare userID's and if a match is found then I want to insert the name inside matches into the entry the match is found in based on the index. However I am not sure how to properly insert a key value pair inside of and object based on index or if im approaching this the correct way. Here is my code:
useEffect(() => {
const loadAllProviders = async () => {
//TODO: Make conditional if entries exists
try {
//get all providers
const results = await api.Providers.listnoAudit({ scope: "all" });
const allProviders = results.items;
//map over the entries to get providerId's out
const providerIDs = entries.map((entry) => entry.createdBy);
//map over response to get out provider names and id's
const idName = allProviders.map((provider) => [provider.id, provider.fullName]);
//find if any returned providers have matching ids to the ones in entries
const matches = idName.filter((match) => providerIDs.includes(match[0]));
//TODO: map over entries
// if matches[0] = entries.createdby then
//push matches[1] (name) into the object at the index where match occurs
entries.map((entry, idx) => {
matches.map((provider) => {
if (entry.createdBy === provider[0]) {
let en = {...entry, fullName: provider[1]}
}
})
});
} catch (err) {}
};
loadAllProviders();
}, [entries]);
Inside my if statement youll see my attempt to add the fullName to the entry using the spread operator but Im not sure how to actually replace the old object with the new object. When I console out en, I get the items that have been modified... I am pretty new to coding and any advice would be helpful.
You can probably reduce your allProviders list to get what you want. See the following example.
let allProviders = [
{id: 1, name: "Amy"},
{id: 2, name: "Kamala"},
{id: 3, name: "Stacy"},
{id: 4, name: "Sunil"}
]
let entries = [{createdBy: 2}, {createdBy: 4}]
let providerIds = entries.map(e => e.createdBy)
let result = allProviders.reduce((matches, provider) => {
if(providerIds.includes(provider.id)) {
matches.push({ ...provider, createdBy: provider.id})
}
return matches;
},[])
console.dir(result)
I am pulling a database query that has the following info:
id, name, roleId, roleTitle
In the query, I am pulling for users and their roles. Each user can have 0 to N number of roles. I want to in the end have an object like this:
{
id
name
roles: [{
id
title
}]
}
What would the most efficient way of doing this be? Currently I am doing something like this:
const data = [];
arr.forEach((u) => {
const index = data.findIndex(x => x.id === u.id);
if (index >= 0) {
data[index].roles.push({ id: u.roleId, title: u.roleTitle });
} else {
data.push({
id: u.id,
name: u.name,
roles: u.roleId ? [{
id: u.roleId,
title: u.roleTitle,
}] : [],
});
}
}
This solution works correctly but wasn't sure if this was the fastest way to get this done if we scale the user numbers to 10k with an average role per user of 3 or 50k and 5 roles per user
Your best bet is actually to do this all in SQL, since you are using PostgreSQL for your database (as mentioned in comments). I don't know the exact names of your tables and columns, so you may need to tweak this, but this will get you what you want:
SELECT json_agg(t)
FROM (
SELECT
u.id,
u.name,
ro.roles
FROM "user" u
LEFT JOIN (
SELECT
ur.user_id,
json_agg(
json_build_object(
'id', r.id,
'title', r.title
)
) AS roles
FROM user_role ur
LEFT JOIN "role" r ON r.id = ur.role_id
GROUP BY ur.user_id
) ro ON ro.user_id = u.id
) t;
SQL Fiddle: http://www.sqlfiddle.com/#!17/5f6ca/11
Explanation
json_build_object will create an object using the name / value pairs specified, so:
json_build_object(
'id', r.id,
'title', r.title
)
Combines the role id and title into a JSON object like this:
{id: 1, title: "Title 1"}
json_agg aggregates multiple rows into a JSON array, so it converts the role objects above into a single column that is an array of role objects per user (thanks to the GROUP BY u.id part of the inner subquery). The inner subquery gives us a result set like this (one row per user)
| user_id | roles |
|---------|------------------------------------------------------|
| 1 | [{id: 1, title: "Role 1"}, {id: 2, title: "Role 2"}] |
Then the subquery is joined to the user table, and all of that is wrapped in another subquery so json_agg can be used on the entire result and return a single json object that is an array of users with roles.
This almost certainly isn't the most efficient possible version but is faster than what you're doing now:
const data = Object.values(arr.reduce((obj, {id, name, roleId, roleTitle}) => {
if (!(id in obj)) {
obj[id] = {
id,
name,
roles: {},
};
}
if (!obj[id].roles[roleId]) {
obj[id].roles[roleId] = {
id: roleId,
title: roleTitle,
};
}
return obj;
}, {}));
By using objects (hashes) instead of arrays, determining if the user is already there or if the user already has a role is a constant-time O(1) operation (the cost of the hashing function). But searching an array, depending on the search method used, is linear in the worst case O(n) and even the best case is O(log n).
You could go down the rabbit hole of micro-optimizations that will change with the wind, but choosing the correct data structures and algorithms will usually get you the most bang for your optimization buck.
I've used Object.values to convert back to an array at the end, if you omit this and just stick with objects it could be even faster.
Hope this helps.
var modified_array = function(xs, key) {
return xs.reduce(function(rv, x) {
obj = (rv[x[key]] = rv[x[key]] || {});
obj.id = x.id;
obj.name = x.name;
obj.roles = obj.roles || []
obj.roles.push({ id: x.roleId, title: x.roleTitle})
return rv;
}, {});
};
arr = [{id:1,name:"abd",roleId: 10,roleTitle: "hello"},
{id:1, name: "abd", roleId: 15,roleTitle: "fello"}]
console.log( Object.values(modified_array(arr, 'id')));