Javascript map operator - javascript

I have an object like this
[
{
_id: '5ef34e92858bff53bcf69e11',
factors: [ {factor_id:'factor_id1',calmode:'calmode1',desc:'desc1',webserv:'webserv1',approach:'approach1'},
{factor_id:'factor_id2',calmode:'calmode2',desc:'desc2',webserv:'webserv2',approach:'approach2'},
{factor_id:'factor_id3',calmode:'calmode3',desc:'desc3',webserv:'webserv3',approach:'approach3'}
],
clientId: 'Company1',
module: 'Mod1',
__v: 0
},
{
_id: '5ef350c9c1acd61e58ef9d08',
factors: [ {factor_id:'factor_id4',calmode:'calmode4',desc:'desc4',webserv:'webserv4',approach:'approach4'},
{factor_id:'factor_id5',calmode:'calmode5',desc:'desc5',webserv:'webserv5',approach:'approach5'}
],
clientId: 'Company1',
module: 'Mod2',
__v: 0
}
]
I want to create a final list like below
_id, ClientId,module,factor_id,calmode,desc,webserv,approach
I am trying to use map operator within another map operator but its not coming out properly. Any help would be appreciated.
const tmpFacLst = FacExists.map((module) => {
const Factor = {
module_id: module._id,
module: module.module,
};
return Factor;
/*const Fac = module.factors.map((factor)=>{
const FactorDtl = {
factor_id:factor._id,
factor_desc: factor.desc
}
return FactorDtl;
})*/
});
Update: I am able to achieve using loop
const result = [];
FacExists.forEach((item) => {
const Factors = item.factors;
Factors.forEach((Subitem) => {
const Facobj = {
_id: item._id,
ClientId: item.clientId,
module: item._id,
factor_id: Subitem._id,
calmode: Subitem.calmode,
desc: Subitem.desc,
webserv: Subitem.webserv,
};
result.push(Facobj);
});
});
I want to know is there any better way of doing this without looping.

An approach like this should work:
const items = [
{
_id: "5ef34e92858bff53bcf69e11",
factors: [
{
factor_id: 2,
calmode: "cal",
desc: "something",
webserv: "10.0.0.0",
approach: "forwards",
},
],
clientId: "Company1",
module: "Mod1",
__v: 0,
},
{
_id: "5ef350c9c1acd61e58ef9d08",
factors: [
{
factor_id: 3,
calmode: "cal",
desc: "something",
webserv: "10.0.0.1",
approach: "forwards",
},
],
clientId: "Company1",
module: "Mod2",
__v: 0,
},
];
const result = [];
items.forEach((item) => {
const { factors, __v, ...rest } = item;
result.push(...factors.map((factor) => ({ ...factor, ...rest })));
});
console.log(result);

First, you need to clean your question up a bit because you have the Object keyword / class listed as elements of your factors array, which you call an "object". You should include those objects in your snippets.
let notAnObj = [
{
_id: '5ef34e92858bff53bcf69e11',
factors: [ {_id: 1234, desc: 'bob loblaw'}],
clientId: 'Company1',
module: 'Mod1',
__v: 0
},
{
_id: '5ef350c9c1acd61e58ef9d08',
factors: [],
clientId: 'Company1',
module: 'Mod2',
__v: 0
}
]
console.log(notAnObject)
let arr= [
{
_id: '5ef34e92858bff53bcf69e11',
factors: [ {_id: 1234, desc: 'bob loblaw'}],
clientId: 'Company1',
module: 'Mod1',
__v: 0
},
{
_id: '5ef350c9c1acd61e58ef9d08',
factors: [],
clientId: 'Company1',
module: 'Mod2',
__v: 0
}
]
const tmpFacLst = arr.map((obj) => {
return {
_id: obj._id,
ClientId: obj.clientId,
module: obj.module,
factors: obj.factors.map(factor => {
return {
_id: factor._id,
desc: factor.desc,
}
}),
calmode: undefined,
webserv: undefined,
approach: undefined
};
});
console.log(tmpFacLst)

you can do it like this
const finalResult = FacExists.reduce((aggregator,fact) => {
let result = fact.factors.map(fac=>{
return {
_id: fact._id,
clientId: fact.clientId,
module: fact.module,
...fac
}})
aggregator = [...aggregator,...result];
return aggregator
},[]);
you will get the desired result in the "finalResult" variable.

Related

Nested array mapping and filtering in javascript

I am designing a system and I have some bottlenecks.
I have user array such like that:
const users = [
{
name: "Jack",
workspaces: [
{
_id: "61216512315615645jbk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
{
_id: "41ss16512315615645bk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
],
},
{
name: "Joe",
workspaces: [
{
_id: "71216512315615645jbk",
permissions: ["CAN_DELETE_WORKSPACE"],
},
],
},
];
And I have activeWorkspace object such like that:
const activeWorkspace = {
name: "W1",
_id: "61216512315615645jbk",
};
I need to filter the objects in the users array whose workspace _id is equal to activeWorkspace _id.
Output must be like that:
{
name: "Jack",
workspaces: [
{
_id: "61216512315615645jbk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
{
_id: "41ss16512315615645bk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
],
}
How can I do that?
In addition:
If we want to return an array, not an object, how should we do it? Like that:
[{
name: "Jack",
workspaces: [
{
_id: "61216512315615645jbk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
{
_id: "41ss16512315615645bk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
],
}]
Thanks
If there is only one match. You need to use find(). Inside of the find method, you want to use some() to look for an _id match.
const users = [
{
name: "Jack",
workspaces: [
{
_id: "61216512315615645jbk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
{
_id: "41ss16512315615645bk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
],
},
{
name: "Joe",
workspaces: [
{
_id: "CHANGED_ID",
permissions: ["CAN_DELETE_WORKSPACE"],
},
],
},
];
const activeWorkspace = {
name: "W1",
_id: "61216512315615645jbk",
};
const active = users.find(function (user) {
return user.workspaces.some( function (workspace) {
return workspace._id === activeWorkspace._id;
});
});
console.log(active);
// Same thing as above, just done with a modern approach
const active2 = users.find(({workspaces}) => workspaces.some(({_id}) => _id === activeWorkspace._id));
console.log(active2);
Now if there could be more than one match (your orginal code before the typo, you would use filter() and some() to find all users that have the workspace in their array.
const users = [
{
name: "Jack",
workspaces: [
{
_id: "61216512315615645jbk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
{
_id: "41ss16512315615645bk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
],
},
{
name: "Joe",
workspaces: [
{
_id: "61216512315615645jbk",
permissions: ["CAN_DELETE_WORKSPACE"],
},
],
},
];
const activeWorkspace = {
name: "W1",
_id: "61216512315615645jbk",
};
const active = users.filter(function (user) {
return user.workspaces.some( function (workspace) {
return workspace._id === activeWorkspace._id;
});
});
console.log(active);
// Same thing as above, just done with a modern approach
const active2 = users.filter(({workspaces}) => workspaces.some(({_id}) => _id === activeWorkspace._id));
console.log(active2);
I adjusted the provided data from Joe so he doesn't have permissions
const users = [{
name: "Jack",
workspaces: [{
_id: "61216512315615645jbk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
{
_id: "41ss16512315615645bk",
permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
},
],
},
{
name: "Joe",
workspaces: [{
_id: "61216512315615645bk",
permissions: ["CAN_DELETE_WORKSPACE"],
}, ],
},
];
const activeWorkspace = {
name: "W1",
_id: "61216512315615645jbk",
};
function findPermittedUser() {
return users.filter(user => {
let hasPermission = false
user.workspaces.forEach(workspace => {
if (activeWorkspace._id == workspace._id) {
hasPermission = true
}
})
return hasPermission
})
}
console.log(findPermittedUser())
You can use map and filter to "filter" out the unwanted ids from the users object. Something like :
const users = [
{
"name": "Jack",
"workspaces": [
{
"_id": "61216512315615645jbk",
"permissions": [
"CAN_DELETE_WORKSPACE",
"CAN_EDIT_PROJECT"
]
},
{
"_id": "41ss16512315615645bk",
"permissions": [
"CAN_DELETE_WORKSPACE",
"CAN_EDIT_PROJECT"
]
}
]
},
{
"name": "Joe",
"workspaces": [
{
"_id": "61216512315615645jbk",
"permissions": [
"CAN_DELETE_WORKSPACE"
]
}
]
}
]
const activeWorkspace = {
name: "W1",
_id: "61216512315615645jbk",
};
const filteredUsers = users.map(item => ({
name : item.name,
workspaces: item.workspaces.filter(user => user._id === activeWorkspace._id)}
));
console.log(filteredUsers);
This should work (tested):
const filteredUsers = users.filter(
user => user.workspaces.reduce(
(acc, workspace) => acc || workspace._id === activeWorkspace._id, false)
)
)
Explanation:
We are using filter and reduce as evident from the code. What the code is doing is pretty simple, first, we want to apply filter on the user array. Now in the filter, we need to define the logic, which should return true whenever our condition happens to be true.
Since we have an array of workspaces, we need to iterate over all of them to check if our activeWorkspace._id exists in any of them. For this, you can use a for loop and return true when you find it, else return false if not. But the functional way of doing it would be to use reduce and initialize the accumulator with false. Every time you access a workspace, you return acc || <our condition>. Notice how if even once our condition returns true, the accumulator becomes true for the rest of the execution of reduce. This is slightly poor in performance since you are not exiting as soon as you have found your workspace._id as you would have done in case of a for loop.
users.map(u => u.workspaces).flat().filter(w => w._id === activeWorkspaceId);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat

how to do Reduce, filtration and nested array work javascript

const merged = [
[
{
_id: "6136096f4255d84bcb4a7144",
user_id: "5fbfa729fc46a415ce5503a6",
device_platform: 'ios',
},
{ user: [Object] }
],
[
{
_id: "613609414255d84bcb4a7122",
user_id: "5fbf6f91aff7f3320a906547",
device_platform: 'ios',
},
{ user: [Object] }
],
[
{
_id: "613709f49223350dfdaec618",
user_id: "5fbfa748fc46a415ce5505f1",
device_platform: 'ios',
},
{
_id: "613609184255d84bcb4a710a",
user_id: "5fbfa748fc46a415ce5505f1",
device_platform: 'ios',
},
{ user: [Object] }
]
]
const user_id = ["5fbfa748fc46a415ce5503a8"];
const { matching, nonMatching } = merged.reduce(
(acc, userRecord) => {
userRecord.JSON.Stringify(user._id) &&
userRecord.JSON.Stringify(user._id).some(_id => user_id.includes(_id))
? acc.matching.push(userRecord)
: acc.nonMatching.push(userRecord);
return acc;
},
{ matching: [], nonMatching: [] }
);
How to filter out from nested array is there any way to fix it with this response please guide
user is coming at object it must contain _id for this I used JSON.Stringify() which seems work isn't ?
Thanks
You do not need JSON.stringify() to process your data structures, you can use Optional chaining (?.) here. However, you can use JSON.stringify() for the full output:
const merged = [
[
{
_id: "6136096f4255d84bcb4a7144",
user_id: "5fbfa729fc46a415ce5503a6",
device_platform: 'ios',
},
{ user: { _id : "5fbfa729fc46a415ce5503a6", name : "xys" } }
],
[
{
_id: "613609414255d84bcb4a7122",
user_id: "5fbf6f91aff7f3320a906547",
device_platform: 'ios',
},
{ user: { _id : "5fbfa729fc46a415ce5503a6", name : "xys" } }
],
[
{
_id: "613709f49223350dfdaec618",
user_id: "5fbfa748fc46a415ce5505f1",
device_platform: 'ios',
},
{
_id: "613609184255d84bcb4a710a",
user_id: "5fbfa748fc46a415ce5505f1",
device_platform: 'ios',
},
{ user: { _id : "5fbfa748fc46a415ce5503a8", name : "xys" } }
]
];
const user_id = ["5fbfa748fc46a415ce5503a8"];
const { matching, nonMatching } = merged.reduce(
(acc, userRecord) => {
if (userRecord.some(object => user_id.includes(object?.user?._id))) {
acc.matching.push(userRecord);
} else {
acc.nonMatching.push(userRecord);
}
return acc;
},
{ matching: [], nonMatching: [] }
);
console.log(JSON.stringify(matching, null, ' '));
console.log(JSON.stringify(nonMatching, null, ' '));

JavaScript group a series of objs

I have the following code, where I have a set of obj b is where the way to group them are defined in a by elements.
I'm having some doubts about how to do it.
For example, is it correct to scroll a or b?
Can you give me a hand?
const a = [{
_id: 0,
elements: ['aasa', 'cccx', 'zzzx', 'sd']
},
{
_id: 1,
elements: ['bb', 'xx']
}
];
const b = [{
_id: 'aasa',
info: "sssas"
},
{
_id: 'bb'
},
{
_id: 'zzzx',
info: "ssss"
},
{
_id: 'cccx',
info: "sss"
},
{
_id: 'xx'
}
];
// result
[
[{
_id: 'aasa',
info: "sssas"
},
{
_id: 'zzzx',
info: "ssss"
},
{
_id: 'cccx',
info: "sss"
}
],
[{
_id: 'bb'
},
{
_id: 'xx'
}
]
];
c = a.map(el => el.elements)
const p = b
//.map(el => el.elements)
.reduce(function(prev, curr) {
//if
prev.push(curr);
return prev;
}, []);
//console.log("End",p)
You can first change b to be a lookup object (here I have used a Map), where each id is a key that points to the object itself:
// Transform `b` into a Map of the form:
Map {
'aasa' => {
_id: 'aasa',
info: "sssas"
},
'bb' => {
_id: 'bb'
}
// ... etc ...
}
Then, for each object in a, you can map over the elements array, and use the id from that object as a key in the lookup object to obtain the associated object with that id. Before you perform a map, you can first filter any keys (ie: ids) that don't exist in the lookup table by using .has on the Map:
const a = [{ _id: 0, elements: ['aasa', 'cccx', 'zzzx', 'sd'] }, { _id: 1, elements: ['bb', 'xx'] } ];
const b = [{ _id: 'aasa', info: "sssas" }, { _id: 'bb' }, { _id: 'zzzx', info: "ssss" }, { _id: 'cccx', info: "sss" }, { _id: 'xx' } ];
const lut = new Map(b.map(obj => [obj._id, obj]));
const res = a.map(({elements}) => elements.filter(key => lut.has(key)).map(key => lut.get(key)));
console.log(res);
This should work
const c = a.map(el1 => {
return b.filter(el2 => {
return el1.elements.includes(el2._id)
})
});

Add/Merge element from from one Object to another Object inside array

I have the following code in my async function
async function test () {
try {
const aucs = await auctions.find({owner: 'owner', place: 'place'}).limit(15);
const item = await Promise.all(aucs.map(async aucs => {
const map = await items.find({id: aucs.item});
return map[0]
}));
--->we are here [1]
} catch (err) {
console.log(err);
}
}
test();
and the point [1] I have two arrays avaliable which contain another objects (both are responces from Mongo) here they are:
aucs = [ { _id: 5c00faa4936359120ceb3632,
auc: 177215422,
item: 130251,
price: 26000000,
lastModified: 1543567955000,
date: 2018-11-30T08:53:56.290Z,
__v: 0 },
{ _id: 5c00faa4936359120ceb363f,
auc: 177215440,
item: 130251,
price: 26000000,
lastModified: 1543567955000,
date: 2018-11-30T08:53:56.290Z,
__v: 0 },... ]
and
item = [ { _id: 5bcd8a6134cdd1223cd3239b,
id: 130251,
name: 'TEST_NAME_1',
__v: 0 },
{ _id: 5bcd8a6134cdd1223cd3239b,
id: 130252,
name: 'TEST_NAME_2',
__v: 0 },...]
And I'd like to add to aucs[i]every element in aucs, item[i].name (name: 'TEST_NAME_1')
Like:
combined = [ { _id: 5c00faa4936359120ceb3632,
auc: 177215422,
item: 130251,
name: 'TEST_NAME_1',
price: 26000000,
lastModified: 1543567955000,
date: 2018-11-30T08:53:56.290Z,
__v: 0 },...]
I'm trying to use for loop with auc[i].name = item[i].name or using aucs.push() but for some unknown reason it wasn't worked for me.
I receive error for .push is not a function and for loop didn't add anything. So maybe someone have any idea?
Note: 1
actually solve one problem with item, mongo returns me array inside array like [ [ { ...} ] ] so I should using return map[0] to fix it.
Note: 2
both of aucs and item are object according to typeof and have .length option (they are both the same length and should be all the time. So they are not promises
Note: 3
let merged = {...aucs, ...item}; returns me
{ '0': { _id: 5bcd8a6134cdd1223cd3239b,
id: 130251,
name: 'JewelCraft',
icon: 'inv_jewelcrafting_70_jeweltoy',
stackable: 1,
isAuctionable: true,
__v: 0 }...
but not what I need to
Would be more superior and faster if you use some aggregation trick here
auctions.aggregate([
{ "$match": { "owner": "owner", "place": "place" }},
{ "$lookup": {
"from": "items",
"let": { "item": "$item" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$id", "$$item"] }}}
],
"as": "item"
}},
{ "$limit": 15 }
])
If I understand correctly, the aim is to create a new collection of aucs like the ones found, each updated to include an item name from the item collection where the item has a matching id.
let aucs = [ { _id: "5c00faa4936359120ceb3632",
auc: 177215422,
item: 130251,
price: 26000000,
lastModified: 1543567955000,
date: "2018-11-30T08:53:56.290Z",
__v: 0 },
{ _id: "5c00faa4936359120ceb363f",
auc: 177215440,
item: 130251,
price: 26000000,
lastModified: 1543567955000,
date: "2018-11-30T08:53:56.290Z",
__v: 0 } ];
item = [ { _id: "5bcd8a6134cdd1223cd3239b",
id: 130251,
name: 'TEST_NAME_1',
__v: 0 },
{ _id: "5bcd8a6134cdd1223cd3239b",
id: 130252,
name: 'TEST_NAME_2',
__v: 0 }];
// create a new collection of aucs modified to include the names of matching items
let combined = [];
aucs.forEach(auc => {
let combinedAuc = Object.assign({}, auc);
combined.push(combinedAuc);
let matchingItem = item.find(i => auc.item === i.id);
if (matchingItem) combinedAuc.name = matchingItem.name
});
console.log(combined)

Mongoose Populate with a condition

in a Node.js App with Mongoose(Mongodb), With this code i fetch users and their books:
Users.find({user_id: req.user.id}).populate('books').exec(..);
Right now i want to fetch users that has an special book. i do like this:
Users.find({user_id: req.user.id}).populate('books',null,{bookname:"harry potter"}).exec(..);
But it doesn't work. With this, my code fetches users with their books of null value and if that condition matches, return them instead of null. In fact most of my users has null value for books. but what i want is that if that condioton in populate section is not matches, do not return that user in result array at all!
What i have to do? i have to do another query or something on results for what i need?
All I can think here is that you are calling it wrong. You do not really show much context here other than that your .populate() parameters do not look correct.
Here is a correct listing as a reproducible example:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
var thingSchema = new Schema({
_id: Number,
name: String
},{ _id: false });
var parentSchema = new Schema({
name: String,
things: [{ type: Number, ref: 'Thing' }]
});
var Thing = mongoose.model( 'Thing', thingSchema ),
Parent = mongoose.model( 'Parent', parentSchema );
mongoose.connect('mongodb://localhost/thingtest');
var things = { "one": 1, "two": 2, "three": 3 };
async.series(
[
function(callback) {
async.each([Thing,Parent],function(model,callback) {
model.remove({},callback);
},callback);
},
function(callback) {
var parentObj = new Parent({ "name": "me" });
async.each(
Object.keys(things).map(function(key) {
return { "name": key, "_id": things[key] }
}),
function(thing,callback) {
var mything = new Thing(thing);
parentObj.things.push(thing._id)
mything.save(callback)
},
function(err) {
if (err) callback(err);
parentObj.save(callback);
}
);
},
function(callback) {
console.log("filtered");
var options = {
path: 'things',
match: { "name": { "$in": ['two','three'] } }
};
Parent.find().populate(options).exec(function(err,docs) {
if (err) callback(err);
console.log(docs);
callback();
});
},
function(callback) {
console.log('unfiltered');
Parent.find().populate('things').exec(function(err,docs) {
if (err) callback(err);
console.log(docs);
callback();
})
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
Which will consistently give results like this:
filtered
[ { _id: 55ec4c79f30f550939227dfb,
name: 'me',
__v: 0,
things:
[ { _id: 2, name: 'two', __v: 0 },
{ _id: 3, name: 'three', __v: 0 } ] } ]
unfiltered
[ { _id: 55ec4c79f30f550939227dfb,
name: 'me',
__v: 0,
things:
[ { _id: 1, name: 'one', __v: 0 },
{ _id: 2, name: 'two', __v: 0 },
{ _id: 3, name: 'three', __v: 0 } ] } ]
So take a good look at your data and your calls. The .populate() call needs to match the "path" and then also provide a "match" to query the documents that are to be populated.
Use elemMatch:
var title = 'Harry Potter';
Users.find({books: {$elemMatch: {name: title}})
.exec(processResults);

Categories