Remove Duplicate from Array of Object [duplicate] - object

This question already has answers here:
Group array items using object
(19 answers)
Closed 5 months ago.
Iam having an array of Object
[
{
REQUEST_TYPE: 3,
E_APN: [ 'internet' ],
value:0
},
{
REQUEST_TYPE: 3,
E_APN: [ 'internet' ],
value:0,
},
{
REQUEST_TYPE: 2,
E_APN: [ 'login' ]
value:0,
}
]
if REQUEST_TYPE of each object is same, then it should merge E_APN as shown below:
[
{
REQUEST_TYPE: 3,
E_APN: [ 'internet','internet' ],
value:0,
},
{
REQUEST_TYPE: 2,
E_APN: [ 'login' ],
value:0
}
]
In tried using filter
const resultArr = dataArr.filter((data,index)=>{
return dataArr.indexOf(data) === index;
})
but not able to push E_APN

var dup = [
{
REQUEST_TYPE: 3,
E_APN: ['internet'],
value: 0
},
{
REQUEST_TYPE: 3,
E_APN: ['internet'],
value: 0,
},
{
REQUEST_TYPE: 2,
E_APN: ['login'],
value: 0,
}
]
var unique = []
var ref = {}
var t = 0;
for (let i = 0; i < dup.length; i++) {
if (unique[ref[dup[i].REQUEST_TYPE]]) {
unique[ref[dup[i].REQUEST_TYPE]].E_APN = (unique[ref[dup[i].REQUEST_TYPE]].E_APN).concat(dup[i].E_APN)
} else {
ref[dup[i].REQUEST_TYPE] = t++;
unique.push(dup[i])
}
}
console.log(unique)

Related

MongoDB: Filter by element of object in a subdocument array in aggregate function

Due to some changes to the schema, I've had a to do refactoring that's broken what was a simple filter in an application, in this instance is isToRead while everything else continues to work.
The document in "Assets" that should be appearing is:
{
"_id": {
"$oid": "ID"
},
"userId": "ID",
"folderId": "ID",
"title": "Title",
"note": "<p><strong>Note.</strong></p>",
"typeOfAsset": "web",
"isFavourite": false,
"createdAt": {
"$date": {
"$numberLong": "1666702053399"
}
},
"updatedAt": {
"$date": {
"$numberLong": "1666702117855"
}
},
"isActive": 3,
"tags": [],
"traits": [
{
"$oid": "6357dae53298948a18a17c60"
}
]
"__v": 0
}
… and the reference document in "Assets_Traits" that I'm attempting to filter against should be:
{
"_id": {
"$oid": "6357dae53298948a18a17c60"
},
"userId": "ID",
"numberOfViews": 1,
"isToRead": true,
"__v": 0
}
I'll share the entire method, which includes the various attempts that — for whatever reason — won't work.
let tags = args.tags ? args.tags.split(',') : []
let tagsToMatch = []
if (tags.length > 0) {
tags.forEach(tag => {
tagsToMatch.push(new mongoose.Types.ObjectId(tag))
})
}
let search = {
...(args.phraseToSearch.length > 0 && {
$search: {
index: 'assets',
compound: {
must: [{
phrase: {
query: args.phraseToSearch,
path: 'title',
slop: 2,
score: { boost: { value: 3 } }
}
}],
should: [{
phrase: {
query: args.phraseToSearch,
path: 'note',
slop: 2
}
}]
}
}
})
}
let project = {
$project: {
_id: 0,
id: '$_id',
userId: 1,
folderId: 1,
title: 1,
note: 1,
typeOfAsset: 1,
isFavourite: 1,
createdAt: 1,
updatedAt: 1,
isActive: 1,
attributes: 1,
preferences: 1,
// ...(args.typeOfAttribute === 'isToRead' && {
// traits: {
// $filter: {
// input: "$traits",
// cond: { $eq: [ "$$this.isToRead", true ] }
// }
// }
// }),
tags: 1,
traits: 1,
score: {
$meta: 'searchScore'
}
}
}
let match = {
$match: {
userId: args.userId,
typeOfAsset: {
$in: args.typeOfAsset === 'all' ? MixinAssets().all : [args.typeOfAsset] // [ 'file', 'folder', 'message', 'note', 'social', 'web' ]
},
...(tagsToMatch.length > 0 && {
tags: {
$in: tagsToMatch
}
}),
...(args.typeOfAttribute === 'isToRead' && {
// $expr: {
// $allElementsTrue: [{
// $map: {
// input: '$traits',
// as: 't',
// in: {
// $and: [
// { $eq: [ "$$t.userId", args.userId ] },
// { $eq: [ "$$t.isToRead", true ] }
// ]
// }
// }
// }]
// }
// $expr: {
// $filter: {
// input: "$traits",
// cond: {
// $and: [
// { $eq: [ "$$this.userId", args.userId ] },
// { $eq: [ "$$this.isToRead", true ] }
// ]
// }
// }
// }
}),
isActive: 3
}
}
let lookup = {}
switch (args.typeOfAttribute) {
case 'areFavourites':
match.$match.isFavourite = true
break
case 'isToRead':
// match.$match.traits = {
// userId: args.userId,
// isToRead: true
// }
// match.$match.traits = {
// $elemMatch: {
// userId: args.userId,
// isToRead: true
// }
// }
// lookup = {
// $lookup: {
// from: 'assets_traits',
// let: { isToRead: '$isToRead' },
// pipeline: [{
// $match: {
// $expr: {
// $eq: [ '$isToRead', true ]
// }
// },
// }],
// localField: 'userId',
// foreignField: 'userId',
// as: 'traits'
// }
// }
break
case 'inTrash':
match.$match.isActive = 2
break
}
let skip = {
$skip: args.skip
}
let limit = {
$limit: args.first
}
let group = {
$group: {
_id: null,
count: { $sum: 1 }
}
}
let sort = {
$sort: {
[args.orderBy]: args.orderDirection === 'asc' ? 1 : -1
}
}
console.info('Queries:getAllAssetsForNarrative()', match.$match)
let allAssets = await Models.Assets.schema.aggregate(
(search.hasOwnProperty('$search')) // Order of criteria is critical to the functioning of the aggregate method.
? [search, project, match, sort, skip, limit]
: [match, project, sort, skip, limit]
// : [match, project, { $unwind: '$traits' }, { $match: { traits: { $elemMatch: { isToRead: true } } } }, sort, skip, limit]
)
let [ totalNumberOfAssets ] = await Models.Assets.schema.aggregate(
(search.hasOwnProperty('$search')) // Order of criteria is critical to the functioning of the aggregate method.
? [search, project, match, group]
: [match, project, group]
// : [match, project, { $unwind: '$traits' }, { $match: { traits: { $elemMatch: { isToRead: true } } } }, group]
)
await (() => {
if (args.phraseToSearch.length > 0) {
const SearchFactory = require('../services/search/search')
const Search = SearchFactory(Models)
Search.insertRecentSearch({
userId: args.userId,
phraseToSearch: args.phraseToSearch.toLowerCase()
})
}
})()
I removed lookup in the final two arrays for the aggregate function because it was becoming too complicated for to me understand what was happening.
Weird thing is, "Tags" match and it's also a reference, while "Assets_Traits" won't return or do anything.
The values for typeOfAsset are: [ 'file', 'folder', 'message', 'note', 'social', 'web' ]
While 'All Assets' is chosen, choosing 'To Read' performs a filter against all types of Assets, and additional filtering would happen when a specific type of Asset is chosen — as explained, this worked before the changes to the schema.
Also, ignore tags because those aren't in use here.
Thoughts appreciated!
You did not provide sample of your input (args) or the constants you use (for example MixinAssets().all which i'm suspecting is problematic).
I constructed my own input for the sake of this answer:
const args = {
typeOfAsset: 'isToRead',
typeOfAttribute: "isToRead",
tagsToMatch: ["tag1", "tag2"],
skip: 0,
first: 1,
orderBy: "_id",
orderDirection: "desc"
}
This produces the following pipeline (using your code):
db.Assets.aggregate([
{
"$match": {
"userId": "123",
"typeOfAsset": {
"$in": [
"isToRead"
]
},
"tags": {
"$in": [
"tag1",
"tag2"
]
},
"isActive": 3
}
},
{
"$project": {
"_id": 0,
"id": "$_id",
"userId": 1,
"folderId": 1,
"title": 1,
"note": 1,
"typeOfAsset": 1,
"isFavourite": 1,
"createdAt": 1,
"updatedAt": 1,
"isActive": 1,
"attributes": 1,
"preferences": 1,
"tags": 1,
"traits": 1,
"score": {
"$meta": "searchScore"
}
}
},
{
"$sort": {
"_id": -1
}
},
{
"$skip": 0
},
{
"$limit": 1
}
])
Which works, as you can see in this Mongo Playground sample.
So what is your issue? As I mentioned I suspect one issue is the MixinAssets().all if args.typeOfAsset === 'all' then you use that value, now if it's an array the match condition looks like this:
typeOfAsset: {
$in: [['web', '...', '...']]
}
This won't match anything as it's an array of arrays, if it's a constant value then again it won't match as the type in the db is different.
I will give one more tip, usually when you want to build a pagination system like this and need both the results and totalResultCount it's common practice to use $facet this way you don't have to execute the pipeline twice and you can improve perfomance, like so:
db.Assets.aggregate([
{
"$match": {
"userId": "123",
"typeOfAsset": {
"$in": [
"isToRead"
]
},
"tags": {
"$in": [
"tag1",
"tag2"
]
},
"isActive": 3
}
},
{
$facet: {
totalCount: [
{
$group: {
_id: null,
count: {
$sum: 1
}
}
}
],
results: [
{
"$project": {
"_id": 0,
"id": "$_id",
"userId": 1,
"folderId": 1,
"title": 1,
"note": 1,
"typeOfAsset": 1,
"isFavourite": 1,
"createdAt": 1,
"updatedAt": 1,
"isActive": 1,
"attributes": 1,
"preferences": 1,
"tags": 1,
"traits": 1,
"score": {
"$meta": "searchScore"
}
}
},
{
"$sort": {
"_id": -1
}
},
{
"$skip": 0
},
{
"$limit": 1
}
]
}
}
])
Mongo Playground

How to filter this array of object in Node.js?

I have some data which looks like this-
[
{
"element": 1,
"id": 1
},
{
"element": 1,
"id": 2
},
{
"element": 2,
"id": 1
},
{
"element": 2,
"id": 2
},
{
"element": 3,
"id": 1
}
]
I have data as above as it is array of object and I want to filter as given below mainly in Node.js where I want to filter with element and return new array. It will be helpful if I get any solution for this.
[
{
"element": 1,
"data": [
{
"element": 1,
"id": 1
},
{
"element": 1,
"id": 2
}
]
},
{
"element": 2,
"data": [
{
"element": 2,
"id": 1
}
]
},
{
"element": 3,
"data": [
{
"element": 3,
"id": 1
}
]
}
]
Okay, so let's get some variables in:
const elementsData = [{ element: 0001, id: 1 }, { element: 0001, id: 2 }, { element: 0001, id: 3 }, { element: 0001, id: 4 }, { element: 0002, id: 1 }, { element: 0002, id: 2 }, { element: 0002, id: 3 }, { element: 0003, id: 1 } ]
First, You'll need to filter out the unique element values:
const uniqueElements = []
elementsData.forEach(datum => {
if (!uniqueElements.includes(datum.element)) {
uniqueElements.push(datum.element)
}
})
Then do groupings by uniqueElement
// loop through the unique Elements
const output = uniqueElements.map(uniqueElement => {
// this will return the object with the specified fields
return {
// element value
element: uniqueElement,
// filter through elementsData for matching elements and save then into an array.
// You can do sort() here if you want to sort them by id, but this is basically it
data: elementsData.filter(elementData => elementsData.element === uniqueElement)
}
})

How to shift element from one array position to another in MongoDB with mongoose?

I have got an array of objects in MongoDB and I was moving a particular element id (i.e 1) from its position to below element having id (i.e 2). So that we can get element with id as 2 -> 1 -> 3.
const arr = [
{
id: 1,
name: 'foo'
},
{
id: 2,
name: 'bar'
},
{
id: 3,
name: 'zoo'
}
]
What I've done is used $pull and $push but it gives ConflictingUpdateOperators and I don't know how to deal with it.
updatedPlan = await Plan.findOneAndUpdate(
{ _id: req.params.id },
{
$pull: {
"arr": {
id: 1
}
},
$push: {
"arr" : {
$each: [{ id: 1, name: 'foo'}],
$position: 1
}
},
);
In MongoDB 4.2 or newer you can update a document with Aggregation Pipeline. Using simple $map on a $range of array indexes you can shuffle these indexes and use $arrayElemAt in order to build a new array:
db.col.update({ _id: req.params.id }, [
{
$set: {
arr: {
$map: {
input: { $range: [ 0, { $size: "$arr" } ] },
in: {
$let: {
vars: {
newIndex: {
$switch: {
branches: [
{ case: { "$eq": [ "$$this", 0 ] }, then: 1 },
{ case: { "$lte": [ "$$this", 1 ] }, then: { $subtract: [ "$$this", 1 ] } },
],
default: "$$this"
}
}
},
in: {
$arrayElemAt: [ "$arr", "$$newIndex" ]
}
}
}
}
}
}
}
])

how to remove null, {}, [] from json object in node js?

The json format is like that:
[
[
{},
{
"Country": "Japan",
"cityName": "tokyo",
"onto": [
{
"level1": "one",
"articles": [
null,
{
"id": "114506604",
"name": "bunya3",
"abc": [
{
"filename": "attachmentsfilename3",
"size": 3
}
],
"image": {}
}
]
}
]
}
],
[
{}
]
]
We can see few null, {} and [{}]. How can we remove it ? By the way I am using node js. I have tried by nnjson
nnjson.removeNull(obj_summary);
But not works object without key.
If we assume that your data is always going to be an array, we can map over it and remove empty arrays and objects from the first level:
const data = [
[
{},
{
Country: 'Japan',
cityName: 'tokyo',
onto: [
{
level1: 'one',
articles: [
null,
{
id: '114506604',
name: 'bunya3',
abc: [
{
filename: 'attachmentsfilename3',
size: 3
}
],
image: {}
}
]
}
]
}
],
[{}]
]
function clean(input) {
return input
.map(item => {
// remove empty arrays
if (Array.isArray(item) && item.length === 0) {
return null
}
// Remove empty objects
if (item instanceof Object && Object.keys(item).length === 0) {
return null
}
return item
})
.filter(item => item)
}
console.log(clean(data))
I found the solution.
To remove null I used:
let retSummary = JSON.parse(stringifySummary, (k, v) => Array.isArray(v) ?
v.filter(e => e !== null) : v);
To remove {} I used
var newArray = parObj.filter(value => Object.keys(value).length !== 0);

Return only the matched objects from document [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 4 years ago.
I am building Node-Js application using Mongoose my question is: Is there a way to return the matched Objects from document instead of the entire object to be more specific I want to return the company_report that contains a date < at 2018-06-10
here is my the example with my code:
[
{
companyName: "example",
"history": [
{
"company_report_result": [
{
"attribut": 1111,
}
],
"date": ISODate("2018-06-06T08:11:00.000Z")
},
{
"company_report_result": [
{
"attribut": 22222,
}
],
"date": ISODate("2018-06-12T08:11:00.000Z")
},
{
"company_report_result": [
{
"attribut": 3333,
}
],
"date": ISODate("2018-06-07T08:11:00.000Z")
}
]
}
]
query:
Campaign.find({ 'table.history.date': { $gt: new Date('2018-06-10') } })
You need to use $filter aggregation operator which gives only the matched element from the array and escapes the other elements
db.collection.aggregate([
{
$match: {
"history.date": {
$gte: new Date('2018-06-10')
}
}
},
{
$project: {
companyName: 1,
history: {
$filter: {
input: "$history",
as: "hist",
cond: {
$gte: [
"$$hist.date",
new Date('2018-06-10')
]
}
}
}
}
}
])
Above query will return
[
{
companyName: "example",
"history": [
{
"company_report_result": [
{
"attribut": 22222,
}
],
"date": ISODate("2018-06-12T08:11:00.000Z")
}
]
}
]

Resources