I have a json data like this.
[{
"name": "Peter",
"age": 30,
"hair color": "brown"
}, {
"name": "Steve",
"age": 55,
"hair color": "blonde"
}, {
"name": "Steve",
"age": 25,
"hair color": "black"
}]
My code it does is, It will identify the duplicate and remove the second occurrence.
The code is :
var qdata = [{
"name": "Peter",
"age": 30,
"hair color": "brown"
}, {
"name": "Steve",
"age": 55,
"hair color": "blonde"
}, {
"name": "Steve",
"age": 25,
"hair color": "black"
}]
data = qdata.filter((obj, pos, arr) => {
return arr.map(mapObj =>
mapObj.name).indexOf(obj.name) == pos;
});
console.log(data);
Output will be :
[
{ name: 'Peter', age: 30, 'hair color': 'brown' },
{ name: 'Steve', age: 55, 'hair color': 'blonde' }
]
Here it deletes the second occurrence and keeps the first one, But what I would like to get is remove the first occurrence and keep the second one
[
{ name: 'Peter', age: 30, 'hair color': 'brown' },
{ name: 'Steve', age: 25, 'hair color': 'black' }
]
How can I do that ? Please help
You can simply reverse the array and do what you want and reverse the result to match with your expected result.
qdata.reverse();
data = qdata.filter((obj, pos, arr) => {
return arr.map(mapObj => mapObj.name).indexOf(obj.name) == pos;
});
console.log(data.reverse());
if you don't want to do that you can use the below code to get desired result.
data = [];
qdata.map((i) => {
index = data.findIndex((u) => u.name === i.name);
if (index >= 0) {
data.splice(index, 1, i);
} else {
data.push(i);
}
});
console.log(data);
Related
This is my MongoDB document:
{
"name": ClassA1
"data": [
{
"first": John,
"second": David"
"age": 21,
"score": 1
},
{
"first": John,
"second": David"
"age": 21,
"score": 1
},
{
"first": John,
"second": David"
"age": 22,
"score": 1
}
]
}
What i am trying to achieve here is i want to find if there is repeating "age" (value 21) in data array i want to sum the score field only and copy the other object but without using the $unwind and $group in MongoDB aggregation.
other fields like first, second can be copied as it is and it will be always be same in my case the only different fields will be age and score. i want to compare repeating age and sum the scores in this case my output should be:
{
"name": ClassA1
"data": [
{
"first": John,
"second": David"
"age": 21,
"score": 2
},
{
"first": John,
"second": David"
"age": 22,
"score": 1
}
]
}
As you can see the first element's score is 2 now which is added from the previous one.
I hope you understand.
Playground: https://mongoplayground.net/p/nyXUMEivMIt
So this is what i have tried:
db.collection.aggregate([
{
$addFields: {
values: {
$reduce: {
input: "$array",
initialValue: [],
in: {
$concatArrays: [
"$$value",
{
$cond: [
{
$in: [
"$$this.age",
"$$value.age"
]
},
[
{
"$sum": {
"$add": [
"$$this.score",
"$$value.score"
]
}
}
],
[
"$$this"
]
]
}
]
}
}
}
}
}
])
this is the error i am getting:
query failed: (Location16554) PlanExecutor error during aggregation :: caused by :: $add only supports numeric or date types, not array
Here is my solution for it. I just used JavaScript to solve the problem. Here we put the first item with new age in the map and then later if we find another person with that same age we add the score to the previous person object and make the current one undefined. After iteration over this array we filter all the undefined item and return it.
Edit:
If you want to run this on mongo playground you need to convert the body of the function to a single line string. Try it here: Mongo playground example
PipeLine = [
{
$match: {
class: "ClassA1",
},
},
{
$project: {
_id: 0,
class: 1,
data: {
$function: {
body: function (data) {
const map = {};
data = data.map((item) => {
if (map[item.age]) map[item.age].score += item.score;
else {
map[item.age] = item;
return item;
}
});
return data.filter((item) => item !== undefined);
},
args: ["$data"],
lang: "js",
},
},
},
},
];
I would like to build a JSON structure as below
{
"employee": {
"hireDate": "01/01/2000",
"serviceDate": "01/01/2000",
"employeeDetails": [
{
"roleName": "Analyst",
"beginDate": "01/01/2000",
"endDate": "12/31/2001",
"status": "Active"
},
{
"roleName": "Developer",
"beginDate": "01/01/2002",
"endDate": "01/01/2021",
"status": "Active"
}
],
"name": [
{
"firstName": "Jason",
"lastName": "Washington"
}
]
}
}
I'm have individual objects information as seperate resultsets from DB2 SQL. I would like to form/ build a JSON structure
Here i use one common key name as employer_id in all table result so it will easy to map all result as per employer id
let employee_details =[{
"employer_id":1,
"roleName": "Analyst",
"beginDate": "01/01/2000",
"endDate": "12/31/2001",
"status": "Active"
},{
"employer_id":1,
"roleName": "Developer",
"beginDate": "01/01/2002",
"endDate": "01/01/2021",
"status": "Active"
}
]
let employee_personal_details =[{
"employer_id":1,
"firstName": "Jason",
"lastName": "Washington"
}]
let employee_work_details = [{
"employer_id":1,
"hireDate": "01/01/2000",
"serviceDate": "01/01/2000"
}]
let employee = employee_work_details.map(details=>{
return {
...details,
employeeDetails: employee_details.filter(_details => _details.employer_id == details.employer_id),
name: employee_personal_details.filter( personal_details => personal_details.employer_id == details.employer_id)
}
})
console.log({employee})
You can use map and reduce to build an array from multiple input arrays.
We match based on some shared id, in this case employeeId.
You could make this behaviour more sophisticated by specifying a join property for each array, let's say name or date of birth.
const employees = [{ id: 1, name: "Mark Ryan" }, { id: 2, name: "Joe Smith" }, { id: 3, name: "Meg Green" }];
const employeeDetails = [{ employeeId: 1, roleName: "Analyst", beginDate: "01/01/2002" }, { employeeId: 1, roleName: "Developer", beginDate: "01/01/2005" }, { employeeId: 2, roleName: "QA", beginDate: "03/05/2015" }, { employeeId: 3, roleName: "Manager",beginDate: "11/08/2010" }];
const contactDetails = [{ employeeId: 1, email: "mark.ryan#example.com" }, { employeeId: 2, phone: "555-009" }, { employeeId: 2, email: "joe.smith#example.com" }, { employeeId: 3, email: "meg.ryan#example.com" }];
const arraysToJoin = [ { employeeDetails } , { contactDetails } ];
const result = employees.map(employee => {
return arraysToJoin.reduce( (acc, obj) => {
acc[Object.keys(obj)[0]] = Object.values(obj)[0].filter(details => acc.id === details.employeeId).map(details => {
const { employeeId, ...rest } = details;
return rest;
});
return acc;
}, employee);
});
console.log("Result:",result);
I have some data in a MongoDb Database, and am accessing it using the NodeJS MongoDb Native Driver.
user: {
_id: ****Example Id****
name: 'Foo bar',
examScores: [
{ subject: 'Geography', score: 80, teacher: 'Mr Foo', date: 'somedate'},
{ subject: 'History', score: 57, teacher: 'Mrs Bar', date: 'somedate'},
{ subject: 'Maths', score: 43, teacher: 'Mrs Fizz', date: 'somedate'},
{ subject: 'Geography', score: 43, teacher: 'Mr Buzz', date: 'somedate'},
{ subject: 'Geography', score: 78, teacher: 'Mr Foo', date: 'somedate'},
{ subject: 'History', score: 41, teacher: 'Mr Buzz', date: 'somedate'}
]
}
I'd like to retrieve the top ten scores/exams per subject. I haven't included more than ten exam attempts in the example for brevity.
Something like:
user: {
_id: ****Example Id****
name: 'Foo bar',
grades: [
{ subject: 'Geography', exams: [
{ score: 80, teacher: 'Mr Foo', date: 'somedate' },
{ score: 78, teacher: 'Mr Foo', date: 'somedate' },
{ score: 43, teacher: 'Mr Buzz', date: 'somedate' }
]
},
{ subject: 'History', exams: [
{ score: 57, teacher: 'Mrs Bar', date: 'somedate' },
{ score: 41, teacher: 'Mr Buzz', date: 'somedate'}
]
},
{ subject: 'Maths', exams: [
{ score: 43, teacher: 'Mrs Fizz', date: 'somedate'}
]
}
]
}
Try this query Hope it helps. I have used $unwind ,and then aggregating the data .
db.collection.aggregate([ {$unwind:"$examScores"},
{$group:{"_id":"$examScores.subject",name: { $first: "$name" }, exams: {
$addToSet:{ teacher: '$examScores.teacher'
,score:'$examScores.score',date:'$examScores.date' } }}},
{$group:{"_id":null,name: { $first: "$name" }, "grades":{$push:
{"subject":"$_id", "exams":"$exams"}}}},
{$project:{"_id":0,"grades":1,"name":1}} ])
the expected output I am getting is on my local machine
{
"name": "Foo bar",
"grades": [
{
"subject": "History",
"exams": [
{
"teacher": "Mr Buzz",
"score": 41,
"date": "somedate"
},
{
"teacher": "Mrs Bar",
"score": 57,
"date": "somedate"
}
]
},
{
"subject": "Maths",
"exams": [
{
"teacher": "Mrs Fizz",
"score": 43,
"date": "somedate"
}
]
},
{
"subject": "Geography",
"exams": [
{
"teacher": "Mr Foo",
"score": 78,
"date": "somedate"
},
{
"teacher": "Mr Buzz",
"score": 43,
"date": "somedate"
},
{
"teacher": "Mr Foo",
"score": 80,
"date": "somedate"
}
]
}
]
}
Please try the below query :
Unwind the examScores array
Sort collection based on subject and scores in descending order since
you need the results for every subject in descending order of scores.
Group results based on the subject
Since you need only top 10 highest scores to be listed for every subject
you can use $Slice operation to limit to first 10 highest scores
for every subject.
db.collection.aggregate([
{ $unwind : "$examScores" },
{ $sort :
{ "examScores.subject" : 1,
"examScores.score" : -1
}
},
{ $group : {
"_id" : "$examScores.subject",
"name" : { $first: "$name" },
"examsAll" : {
$addToSet : {
teacher: '$examScores.teacher',
score : '$examScores.score',
date : '$examScores.date'
}
}
}
},
{ $project : {
"_id" : 1,
"name" : 1,
"exams" : { $slice: [ "$examsAll", 10 ] }
}
},
{ $group : { "_id" : null,
"name" : { $first: "$name" },
"grades" : { $push:
{ "subject" : "$_id",
"exams" : "$exams"
}
}
}
},
{ $project : { "_id" : 0,
"grades" : 1,
"name" : 1
}
}
]);
According with MongoDB documentation (http://docs.mongodb.org/manual/reference/operator/projection/elemMatch/):
{
_id: 1,
school: "school A",
zipcode: 63109,
students: [
{ name: "john", school: 102, age: 10 },
{ name: "jess", school: 102, age: 11 },
{ name: "jeff", school: 108, age: 15 }
]
}
{
_id: 2,
school: "school B",
zipcode: 63110,
students: [
{ name: "ajax", school: 100, age: 7 },
{ name: "achilles", school: 100, age: 8 },
]
}
{
_id: 3,
school: "school C",
zipcode: 63109,
students: [
{ name: "ajax", school: 100, age: 7 },
{ name: "achilles", school: 100, age: 8 },
]
}
{
_id: 4,
school: "school D",
zipcode: 63109,
students: [
{ name: "barney", school: 102, age: 7 },
]
}
launching:
schools.find({ zipcode: 63109}, {students: { $elemMatch: { school: 102
} } }, function (err, school) { ...}
The operation returns the following documents:
{ "_id" : 1, "students" : [ { "name" : "john", "school" : 102, "age" :
10 } ] } { "_id" : 3 } { "_id" : 4, "students" : [ { "name" :
"barney", "school" : 102, "age" : 7 } ] }
But I need the value of school filed too...
{ "_id" : 1, "school": "School A", "students" : [ { "name" : "john", "school" : 102, "age" :
10 } ] } { "_id" : 3, "school": "School C" } { "_id" : 4, "school": "School D", "students" : [ { "name" :
"barney", "school" : 102, "age" : 7 } ] }
and I can't find a way to achieve this...
http://docs.mongodb.org/manual/reference/method/db.collection.find/
If the projection argument is specified, the matching documents
contain only the projection fields and the _id field. You can
optionally exclude the _id field.
but... I forced the Fields to Return using:
schools.find({ zipcode: 63109}, {school: 1, students: { $elemMatch: { school: 102 } } }, function (err, school) { ...}
and all seems to work properly...
According to mongodb [documentation][1] $elemMatch return first matching element from an array based on a condition. So you have to use $filter instead of $elemMatch to get all matching element.
I have written a solution. please take a look.
solution checkup link: https://mongoplayground.net/p/cu7Mf8XZHDI
db.collection.find({},
{
students: {
$filter: {
input: "$students",
as: "student",
cond: {
$or: [
{
$eq: [
"$$student.age",
8
]
},
{
$eq: [
"$$student.age",
15
]
}
]
}
}
}
})
[1]: http://docs.mongodb.org/manual/reference/operator/projection/elemMatch/
New to couchdb, I want to do a 1 to Many relationship with sorting
Category -> Documents
Would like to sort Category and sort Documents within each Category and hopefully get the result as an array.
I can sort either by Category or by Documents, but not both.
I would like to get something like this as the query result:
[{ name: 'category1',
position: 1,
type: 'category',
documents: [{ name: ..., type: ..., position: 1 },
{ name: ..., type: ..., position: 2 },
{ name: ..., type: ..., position: 3 }
}
{ name: 'category2',
position: 2,
type: 'category',
documents: [{ name: ..., type ..position: 1 },
{ name: ..., position: 2 },
{ name: ..., position: 3 }]
}]
I setup a view design and a map function like so (is this the correct approach?):
function(category) {
if (type == 'category') {
for (var d in category.documents) {
emit([category.position, category.documents[d].position??? ], {_id: category.documents[d], category: category })
}
}
Problems are...
1- category.documents[d].position wouldn't 'exist' yet so I can't simply do that.
2- the query results isn't formatted the way I would want. It would be rows of documents instead of rows of category with a array of document objects.
There are no relationships in CouchDB. The correct way would be to set the category a document belongs to directly on the document.
{
_id: ...,
name: ...,
type: ...,
category: 1,
position: 1
}
As pointed out by #OctavianDamiean, you should add a category field in the document. Then the map function becomes something like:
function(doc) {
if (doc.type === 'Document') {
emit([doc.category, doc.position], 1);
}
}
Querying with include_docs=true, you'll get:
[ { "key": ["category1", 1], "doc": { "name": "doc1.1", "type": "Document", "position": 1 } },
{ "key": ["category1", 2], "doc": { "name": "doc1.2", "type": "Document", "position": 2 } },
{ "key": ["category2", 1], "doc": { "name": "doc2.1", "type": "Document", "position": 1 } },
{ "key": ["category2", 2], "doc": { "name": "doc2.2", "type": "Document", "position": 2 } },
...
]