Loop inside for loop - nested arrays - javascript

I have for loop inside for loop... and my array output is nested array [[{...}]] but I need
[{...}]
Is there any way to make this array without nested array in this case?
Here is my code...
I have users and for every user i need to add new array of objects
users = [{
name: "user1"
role: ["admin", "tester", "viewer"]
},{
name: "user2"
role: ["admin", "tester", "viewer"]
}]
userRole: any[]=[];
for (let user of users){
for (let u of user.role){
this.userRole.push({
name: user.name,
role: u
})
}
this.data.push(this.userRole)
}
User can have multiple role, but because of database structure I need in user object send array with object
{ "name": "user1",
"role": [{
"role" : "admin",
"user" : "user1"
},{
"role" : "tester",
"user" : "user1"
},{
"role" : "view",
"user" : "user1"
}]
}
but I get this
{ "name": "user1",
"role": [ // nested array
[{
"role" : "admin",
"user" : "user1"
},{
"role" : "tester",
"user" : "user1"
},{
"role" : "view",
"user" : "user1"
}]
]
}
I try to use .flat() but still the same

You can simply use two map
let users = [{name: "user1",role: ["admin", "tester", "viewer"]}, {name: "user2",role: ["admin", "tester", "viewer"]}]
let final = users.map(({name,role})=>{
return {
name,
role: role.map(user=>({name,user}))
}
})
console.log(final)
To make your code work, all you need is defined two variable one to hold the final value and another one to hold userRole for particular user,
const users = [{name: "user1",role: ["admin", "tester", "viewer"]}, {name: "user2", role: ["admin", "tester", "viewer"]}]
const final = []
for (let user of users) {
const userRole = [];
for (let u of user.role) {
userRole.push({
name: user.name,
role: u
})
}
final.push({name:user.name, role: userRole})
}
console.log(final)

I made some change on your code. Every loop you are creating a new role, I supposed this.data is a array of userRole? So, you can add it every loop on your data array.
for (let user of users){
for (let u of user.role){
const userRole= { name: user.name, role: u};
this.userRole.push(userRole);
this.data.push(userRole)
}
}

Please use flatMap :
var data = { "name": "user1",
"role": [ // nested array
[{
"role" : "admin",
"user" : "user1"
},{
"role" : "tester",
"user" : "user1"
},{
"role" : "view",
"user" : "user1"
}]
]
};
data.role = data.role.flatMap(data => data);
console.log("print data object");
console.log(data)

users = [{
name: "user1",
role: ["admin", "tester", "viewer"],
},{
name: "user2",
role: ["admin", "tester", "viewer"],
}
]
userRole = users.map(user => {
roles = user.role.map(x => {
return {role:x,user:user.name}
})
return {
name: user.name,
role: roles
}
})
console.log(userRole)

Why don't you use the "u" in the second for loop, to add your array of roles?

Here is the answer, thnx to user #CodeManiac, he gave me a way of thinking...
I need to push item inside second for-loop... and now everything works like I expect
for (let user of users){
for (let u of user.role){
this.data.push(name: user.name, role: u)
}
}

Related

MongoDB aggregation: Count documents in a query for each array field

Here's an example through JS code of what I'm trying to achieve:
let waiting = findSessions() // regular query for status "WAITING"
let results = [];
for (let w of waiting) {
// Only push it to results if the w.members[0] and TARGET_USER_ID have never matched before.
// Meaning, in the "session" collection, there are no documents that have these 2 IDs in the members field
if (!hasMatchedBefore(w.members[0], "TARGET_USER_ID")) {
results.push(w);
}
}
IGNORE MOST OF WHAT'S ABOVE
Just realized how poorly written the old question was. Let's start from the beginning.
Consider the collection "sessions":
[
{
status: "WAITING",
type: "TEXT",
members: [
"adam"
]
},
{
status: "WAITING",
type: "TEXT",
members: [
"john"
]
},
{
status: "WAITING",
type: "VOICE",
members: [
"alex"
]
},
{
status: "ENDED",
type: "VOICE",
members: [
"adam",
"alex"
]
},
{
status: "TIMEOUT",
type: "TEXT",
members: [
"adam",
"TARGET"
]
}
]
I'm making a match-matching system. Let's say "TARGET" wants to match. I'm trying to write a MongoDB aggregation that does the following.
Find all documents with query { type: "TEXT", status: "WAITING" }
Iterate through each document: check if members[0] and TARGET have ever matched before
If members[0] and TARGET have matched before (check entire collection, any type/status), then it will not be included in the final array
It should return the following:
[
{
status: "WAITING",
type: "TEXT",
members: [
"john"
]
},
]
Notice how there were 3 "WAITING" rooms in the collection. But TARGET had already matched with adam. And TARGET cannot match with alex, because alex is in a "VOICE" session. So in this case, john would be the only appropriate match.
One option is to use $lookup on the same collection:
db.sessions.aggregate([
{$match: {
status: "WAITING",
type: "TEXT",
"members.0": {$ne: target}
}},
{$lookup: {
from: "sessions",
let: {member: {$first: "$members"}},
pipeline: [{$match: {$expr: {$setIsSubset: [["$$member", target], "$members"]}}}],
as: "found"
}},
{$match: {"found.0": {$exists: false}}},
{$group: {
_id: 0,
members: {$push: {$arrayElemAt: ["$members", 0]}},
status: {$first: "$status"},
type: {$first: "$type"}
}}
])
See how it works on the playground example
I think, I have a solution for you.
Data
[
{
_id: "ObjectId1",
status: "WAITING",
"members": [
"ID1"
]
},
{
_id: "ObjectId2",
status: "WAITING",
"members": [
"ID2"
]
},
{
_id: "ObjectId3",
status: "ENDED",
"members": [
"ID1",
"ID2"
]
}
]
Query
db.collection.find({
status: "ENDED",
members: {
$elemMatch: {
$eq: "ID2"
}
}
})
Output
[
{
"_id": "ObjectId3",
"members": [
"ID1",
"ID2"
],
"status": "ENDED"
}
]
Note: Please check mongoplayground link.
Main Solution: https://mongoplayground.net/p/xkyyW8gsWV6
Other Solution: https://mongoplayground.net/p/1ndltdDU38-

how to get the two collections data into one output in mongodb using node js

These are my two collections in MongoDB, and when I use $lookup to get the details my result comes with a nested array. I need the result to be like below:
First collection
locations = [{
locationName : "DC",
locationCategoryCode:"1"
}, {
locationName : "New York",
locationCategoryCode:"1"
}, {
locationName : "Utah",
locationCategoryCode:"2"
}]
Second collection
locationCategory = [{
locationCategoryCode:"1",
locationCategoryName:"history"
}, {
locationCategoryCode:"2",
locationCategoryName:"nature"
}]
Result
result = [{
locationName : "DC",
locationCategoryName:"history"
}, {
locationName : "NewYork",
locationCategoryName:"history"
}]
Any solution for this, please and thank you?
Using aggregation you could:
$lookup the category, returning an array
$addFields to add the category name to the top level document
$project to remove the added array and the location code
db.locations.aggregate([
{$lookup: {
from: "locationCategory",
localField: "locationCategoryCode",
foreignField: "locationCategoryCode",
as: "locationCategory"
}},
{$addFields: {
locationCategoryName: {
$arrayElemAt: ["$locationCategory.locationCategoryName",0]
}
}},
{$project: {
locationCategory: 0,
locationCategoryCode: 0
}}
])
Playground
You can find the associated locationCategoryName of each location by searching locationCategory using the locationCategoryCode.
const locations = [{
locationName: "DC",
locationCategoryCode: "1"
}, {
locationName: "New York",
locationCategoryCode: "1"
}, {
locationName: "Utah",
locationCategoryCode: "2"
}];
const locationCategory = [{
locationCategoryCode: "1",
locationCategoryName: "history"
}, {
locationCategoryCode: "2",
locationCategoryName: "nature"
}];
const result = locations
.map(({ locationName, locationCategoryCode }) => ({
locationName,
locationCategoryName: locationCategory.find(({ locationCategoryCode: curr }) =>
curr === locationCategoryCode).locationCategoryName
}));
console.log(result);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Merge 2 object arrays in json files by key

I'm trying to combine 2 object array in javascript/jquery matching them by the same key (code). These object arrays are stored in 2 separate json files.
I've cut these down as the files are long
Thanks in advance if anyone can help.
Object 1:
[{
"city": "london",
"selfemployed": {
"job" :"Builder",
"code": "abc"
},
"company" : {
"job": "Shopkeeper",
"code": "def"
}
}]
Object 2:
[{
"code": "abc",
"participant": {
"firstname" : "Joe",
"lastname" : "Blogs"
}
},
{
"code": "def",
"participant": {
"firstname" : "Anna",
"lastname" : "Smith"
}
}]
Needed result:
[{
"city": "london",
"selfemployed": {
"job" :"Builder",
"code": "abc",
"participant": {
"firstname" : "Joe",
"lastname" : "Blogs"
}
},
"company" : {
"job": "Shopkeeper",
"code": "def",
"participant": {
"firstname" : "Anna",
"lastname" : "Smith"
}
}
}]
One of my issues is that I'm unable to return the object from the .json files
var file1 = 'url/file1.json';
var file1 = 'url/file2.json';
const joinJson = (file1, file2) => {
$.getJSON(file, function(data1) {
return data1;
});
$.getJSON(file2, function(data2) {
return data2;
});
// do stuff with data1 and data2
}
console.log(joinJson());
You could take a Map and build new objects for the result by selecting the wanted code information for the new object.
This proposal uses rest properties of an object with babel for older user agents.
var cities = [{ city: "london", selfemployed: { job: "Builder", code: "abc" }, company: { job: "Shopkeeper", code: "def" } }],
codes = [{ code: "abc", participant: { firstname: "Joe", lastname: "Blogs" } }, { code: "def", participant: { firstname: "Anna", lastname: "Smith" } }],
codesMap = new Map(codes.map(({ code, participant }) => [code, participant])),
result = cities.map(
({ city, ...items }) =>
Object.assign({ city }, ...Object.entries(items).map(
([k, v]) => ({ [k]: Object.assign({}, v, codesMap.get(v.code)) })
))
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The problem is that you're getting the files asynchronously, and returning data in the callback of that async operation, which goes nowhere. Something like this would be better:
var file1 = 'url/file1.json';
var file1 = 'url/file2.json';
const joinJson = (file1, file2) => {
$.getJSON(file, function(data1) {
// async operation 1 complete. Start operation 2.
$.getJSON(file2, function(data2) {
// async operation 2 complete
// data1 and data2 are now available here
console.log("Data 1:", data1);
console.log("Data 2:", data2);
// This will merge the keys of both objects
// but you can replace it with custom merging logic
var merged = Object.assign(data1, data2);
console.log(merged);
});
});
}
The problem here is that you can't do something like console.log(joinJson());. You may very well need something like a Promise.

mongoose get relative data without query

I have this two schema, fruits schema and user schema. I split them in 2 different collection, but a fruit schema can have a reference to a user.
const FruitSchema = new Schema({
title: {
type: String
},
buyer: {
_id: {
type: Schema.Types.ObjectId,
default: null
}
}
})
const UserSchema = new Schema({
name: {
type: String
},
age: {
type: Number
},
gender: {
type: String
}
})
How would you query all fruits with the user info? What I can think of is find all fruits, get the buyer._id then find each user, map them back the fruit array, sounds so tedious and complicated. If it's mysql I just JOIN.
You need to use $lookup to join buyer id with user id, for all matching collections, it will return the user info as an embedded document
db.fruitz.aggregate(
[
{
$lookup : {
from : "userz",
localField : "buyer_id",
foreignField: "_id",
as : "buyerInfo"
}
}
]
)
fruits collection
> db.fruitz.find().pretty()
{ "_id" : 2, "title" : "apple", "buyer_id" : 1 }
{ "_id" : 1, "title" : "banana", "buyer_id" : 2 }
users collection
> db.userz.find().pretty()
{ "_id" : 1, "name" : "abc", "age" : 20, "gender" : "M" }
>
$lookup aggregate
> db.fruitz.aggregate( [ { $lookup : { from : "userz", localField : "buyer_id", foreignField: "_id", as : "buyerInfo" } } ] ).pretty()
output
{
"_id" : 2,
"title" : "apple",
"buyer_id" : 1,
"buyerInfo" : [
{
"_id" : 1,
"name" : "abc",
"age" : 20,
"gender" : "M"
}
]
}
{ "_id" : 1, "title" : "banana", "buyer_id" : 2, "buyerInfo" : [ ] }
>
MongoDB Provides $lookup to join records from two collections:
In your case you can join fruits and user using $lookup:
db.fruits.aggregate([
{
$lookup:
{
from: "fruits",
localField: "buyer",
foreignField: "_id",
as: "buyer_info"
}
}
])
$lookup hat got more powerful in 3.6(if you're using 3.6) which allows adding expressions and on the things you're joining from right collection

MongoDB $project embedded document to root level

Using the aggregate pipeline, I am trying to project an embedded document to the root level WITHOUT projecting each field individually.
For example, I want to project name from this collection to the root level:
[
{
_id: "1",
name: {
firstName: "John",
lastname: "Peters"
}
},
{
_id: "2",
name: {
firstName: "Mary",
lastname: "Jones"
}
}
]
This is what I am looking for:
[
{
firstName: "John",
lastname: "Peters"
},
{
firstName: "Mary",
lastname: "Jones"
}
]
Is there a way to do this without projecting each field individually? I don't want to have to do this:
db.collection.aggregate(
[
{
$project : {
"_id" : 0,
"firstName" : "$name.firstName",
"lastName" : "$name.lastName"
}
}
]
MongoDB 3.4 has the new stage in aggregation pipeline - $replaceRoot, which does exactly what was asked.
https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/
Here is the solution which uses JavaScript variable.
# Set Object for what to project
var projectWhat = {'_id' : 0};
# Fill Object with keys
Object.keys(db.coll.findOne().name).forEach(function(x){
projectWhat[x] = "$name." + x;
});
# Do Aggregate
db.coll.aggregate([{$project : projectWhat}])
And the output will be
{ "firstName" : "John", "lastname" : "Peters" }
{ "firstName" : "Mary", "lastname" : "Jones" }
Hope this helps.
You can use $replaceRoot like this:
db.collection.aggregate(
[
{
$replaceRoot : {
newRoot: {"$name"}
}
}
]
)
Also if you have a field in the root document you want to retain you can use a $mergeObjects to combine it with your embedded object:
db.collection.aggregate(
[
{
$replaceRoot : {
newRoot: {
$mergeObjects: [
{"_id": "$_id"},
"$name"
]
}
}
}
]
)
This may be achieved by using $set to update all documents with the values in the name sub-document:
db.collection.find({ "name": {"$exists": 1 } }).forEach(function(doc) {
var setName = {};
for ( var k in doc.name ) {
setName[k] = doc.name[k];
}
db.collection.update(
{ "_id": doc._id },
{ "$set": setName, "$unset": "name" }
);
})
While I'll recommend you use $project because it would be more performant than this solution, I can understand why you wouldn't want to use $project.
Starting Mongo 4.2, the $replaceWith aggregation operator can be used to replace a document by another (in our case by a sub-document):
// { _id: "1", name: { firstName: "John", lastname: "Peters" } }
// { _id: "2", name: { firstName: "Mary", lastname: "Jones" } }
db.collection.aggregate({ $replaceWith: "$name" })
// { firstName: "John", lastname: "Peters" }
// { firstName: "Mary", lastname: "Jones" }

Categories

Resources