I have data like this
[{"name": "swimming"},{"name": "Politics"},{"name": "Gamer"}]
and I have profiles like
[
{
"username":"abc",
"passions":[
{
"name":"Snooker"
}
]
},
{
"username":"abc",
"passions":[
{
"name":"Coding"
}
]
},
{
"username":"xyz",
"passions":[
{
"name":"swimming"
}
]
},
{
"username":"abc",
"passions":[
{
"name":"Politics"
},
{
"name":"swimming"
}
]
}
]
What I need to do is show first those profiles whose passions are matching with that first data array and then the other rest remaining will show.
I'm sure there are better ways to do this, but this works.
final passions = [{"name": "swimming"},{"name": "Politics"},{"name": "Gamer"}];
final users =
[
{
"username":"abc",
"passions":[
{
"name":"Snooker"
}
]
},
{
"username":"efg",
"passions":[
{
"name":"Coding"
}
]
},
{
"username":"hij",
"passions":[
{
"name":"swimming"
}
]
},
{
"username":"klm",
"passions":[
{
"name":"Politics"
},
{
"name":"swimming"
}
]
}
];
var matches = [];
users.forEach((u) {
passions.forEach((p) {
if (p['name'] == (u['passions'] as List)[0]['name']) {
matches.add(u['username']);
}
});
});
print(matches.toString());
Just give this function a profile list and
Finally returns the sort list
List passions = [{"name": "swimming"}, {"name": "Politics"}, {"name": "Gamer"} ];
List sortListPrfile(List profiles) {
//profiles is a list of result server
List _items = []; // It will be returned eventually
List _noItemCommon = []; //Items that are not common
for (var profile in profiles) {
for (var passion in passions) {
if (profile["passions"][0]["name"] == passion['name']) {
_items.add(profile);
} else {
_noItemCommon.add(profile);
}
}
}
_items.addAll(_noItemCommon);
return _items;
}
Here is a function that returns the complete list by matches first and by sorted users.
Your code has a nested list object that needs to check for values that way is function has one line more :)
Note: This function is only for your current item which matches object keys. To make it more secure you should add guards.
import 'package:flutter/foundation.dart';
void main() {
final filtered = filteredByMatchesFirst(arr, matches);
for (var value in filtered) {
print(value);
}
}
var matches = <Map<String, dynamic>>[
{"name": "swimming"},
{"name": "Politics"},
{"name": "Gamer"},
];
var arr = <Map<String, dynamic>>[
{
"username": "abc",
"passions": [
{"name": "Snooker"}
]
},
{
"username": "abc",
"passions": [
{"name": "Coding"}
]
},
{
"username": "xyz",
"passions": [
{"name": "swimming"}
]
},
{
"username": "byz",
"passions": [
{"name": "Gamer"}
]
},
{
"username": "abc",
"passions": [
{"name": "Politics"},
{"name": "swimming"}
]
}
];
List<Map<String, dynamic>> filteredByMatchesFirst(
List<Map<String, dynamic>> list,
List<Map<String, dynamic>> matches,
) {
// Sort list by users first.
list.sort((m1, m2) {
return (m1['username'] as String).compareTo(m2['username'] as String);
});
// Filter only match values.
var filtered = list.where((map) {
final passions = map['passions'] as List<Map<String, dynamic>>;
return passions.any((a) => matches.any((b) => mapEquals(a, b)));
}).toList();
// Add the rest to the filtered list without duplicates.
return filtered.followedBy(list).toSet().toList();
}
Output:
{username: abc, passions: [{name: Politics}, {name: swimming}]}
{username: byz, passions: [{name: Gamer}]}
{username: xyz, passions: [{name: swimming}]}
{username: abc, passions: [{name: Snooker}]}
{username: abc, passions: [{name: Coding}]}
Output without matching:
{username: abc, passions: [{name: Snooker}]}
{username: abc, passions: [{name: Coding}]}
{username: abc, passions: [{name: Politics}, {name: swimming}]}
{username: byz, passions: [{name: Gamer}]}
{username: xyz, passions: [{name: swimming}]}
Related
I have a list of Maps. The below is the list of maps,
List teachers = [
{
'name':'John',
'subject':'English'
},
{
'name':'Rohan',
'subject':'Hindi'
},
{
'name':'Benny',
'subject':'English'
},
{
'name':'Rose',
'subject':'Tamil'
},
{
'name':'Shine',
'subject':'Kannada'
},
{
'name':'Tintu',
'subject':'English'
}
];
From this I want to keep any of the one english teacher and remove all other teacher with subject english.
The below is the expected result.
List teachers = [
{
'name':'John',
'subject':'English'
},
{
'name':'Rohan',
'subject':'Hindi'
},
{
'name':'Rose',
'subject':'Tamil'
},
{
'name':'Shine',
'subject':'Kannada'
},
];
You can use collection package and try this:
var grouped =
groupBy(teachers, (Map<String, String> value) => value['subject']);
List<Map> result = grouped.entries.map((e) => e.value.first).toList();
print("result =$result");//result =[{name: John, subject: English}, {name: Rohan, subject: Hindi}, {name: Rose, subject: Tamil}, {name: Shine, subject: Kannada}]
I have this list and want to sum value and remove duplicates in List
1 - check of productName
2 - sum NumberOfItems if productName equals
For Example :
"Orders":[
{
"productName":"Apple",
"NumberOfItems":"5"
},
{
"productName":"Orange",
"NumberOfItems":"2"
},
{
"productName":"Egg",
"NumberOfItems":"5"
},
{
"productName":"Apple",
"NumberOfItems":"3"
},
{
"productName":"Orange",
"NumberOfItems":"4"
},
{
"productName":"Egg",
"NumberOfItems":"9"
},
]
The result I need look like this result : (Sum Depend on productName)
"Orders":[
{
"productName":"Apple",
"NumberOfItems":"8"
},
{
"productName":"Orange",
"NumberOfItems":"6"
},
{
"productName":"Egg",
"NumberOfItems":"14"
},
]
final orders = data["Orders"] as List;
final mapped = orders.fold<Map<String, Map<String, dynamic>>>({}, (p, v) {
final name = v["productName"];
if (p.containsKey(name)) {
p[name]["NumberOfItems"] += int.parse(v["NumberOfItems"]);
} else {
p[name] = {
...v,
"NumberOfItems": int.parse(v["NumberOfItems"])
};
}
return p;
});
final newData = {
...data,
"Orders": mapped.values,
};
print(newData);
Result is:
{Orders: ({productName: Apple, NumberOfItems: 8}, {productName: Orange, NumberOfItems: 6}, {productName: Egg, NumberOfItems: 14})}
Notice: This code has 2 loop which means slower.
Igor Kharakhordin answered smarter one, but may be difficult for those who ask this question.(since he is doing two things at once.) Basically I am doing same thing.
String string = await rootBundle.loadString("asset/data/Orders.json");
Map orders = jsonDecode(string);
List orderList = orders["Orders"];
Map<String,int> sums = {};
for(int i = 0 ; i < orderList.length; i++){
dynamic item = orderList[i];
if(sums.containsKey(item["productName"])){
sums[item["productName"]] += int.parse(item["NumberOfItems"]);
}
else{
sums[item["productName"]] = int.parse(item["NumberOfItems"]);
}
}
List sumList = [];
sums.forEach((key,value)=>
sumList.add({
"productName":key,
"NumberOfItems":value.toString()
})
);
Map result = {
"Orders":sumList
};
print(jsonEncode(result));
Result
{
"Orders": [
{
"productName": "Apple",
"NumberOfItems": "8"
},
{
"productName": "Orange",
"NumberOfItems": "6"
},
{
"productName": "Egg",
"NumberOfItems": "14"
}
]
}
How do I add an item to Mongoose, if I want to push it to an item of the array?
I want to push it to the document with predefined _id, to the 'productList' array with predefined 'id', to the 'items' array.
{
"_id" : ObjectId("5ba94316a48a4c828788bcc9"),
"productList" : [
{
"id" : 1,
"items" : [
{
"id" : 1,
"name" : "FLOSS 500",
}
]
}
]
}
I thought that it should be something like this, but it did not work:
Products.findOneAndUpdate({_id: req.body._id, productList: {id: req.body.id}}, {$push: {'items': req.body.product}})
You can try this with positional operator $. For search by nested array property use dot-separated syntax:
Products.findOneAndUpdate({
_id: req.body._id,
'productList.id': req.body.id
}, { $push: { 'productList.$.items': req.body.product } });
Full example:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Products = mongoose.model('Test', new Schema({
productList: []
}));
mongoose.connect("mongodb://localhost:27017/myapp");
let item = new Products({
"_id": mongoose.Types.ObjectId("5ba94316a48a4c828788bcc9"),
"productList": [
{
"id": 1,
"items": [
{
"id": 1,
"name": "FLOSS 500",
}
]
}
]
});
Products.deleteMany({}).then(() => {
return Products.create(item);
}).then(() => {
return Products.findOneAndUpdate({
_id: mongoose.Types.ObjectId("5ba94316a48a4c828788bcc9"),
'productList.id': 1
}, {
$push: {
'productList.$.items': {
"id": 2,
"name": "FLOSS 600",
}
}
});
}).then(() => {
return Products.find({
_id: mongoose.Types.ObjectId("5ba94316a48a4c828788bcc9"),
'productList.id': 1
});
}).then(data => {
console.log(data);
if (data) {
console.log(data[0].productList);
/* [{"id":1,"items":[{"id":1,"name":"FLOSS 500"},{"id":2,"name":"FLOSS 600"}]}] */
}
}).catch(err => {
console.error(err);
});
I have a collection named User, which contains the the fields firstName and secondName. But the data is in capital letters.
{
firstName: 'FIDO',
secondName: 'JOHN',
...
}
I wanted to know whether it is possible to make the field to camel case.
{
firstName: 'Fido',
secondName: 'John',
...
}
You can use a helper function to get your desired answer.
function titleCase(str) {
return str.toLowerCase().split(' ').map(function(word) {
return word.replace(word[0], word[0].toUpperCase());
}).join(' ');
}
db.User.find().forEach(function(doc){
db.User.update(
{ "_id": doc._id },
{ "$set": { "firstName": titleCase(doc.firstName) } }
);
});
Run an update operation with aggregate pipeline as follows:
const titleCase = key => ({
$concat: [
{ $toUpper: { $substrCP: [`$${key}`,0,1] } },
{ $toLower: {
$substrCP: [
`$${key}`,
1,
{ $subtract: [ { $strLenCP: `$${key}` }, 1 ] }
]
} }
]
});
db.User.updateMany(
{},
[
{ $set: {
firstName: titleCase('firstName'),
secondName: titleCase('secondName')
} }
]
)
Mongo Playground
How do I get the email address of the students in the same class_id, take it as there are more then 2 students in different class in the DB as well?
I have this but it return empty array []
Meteor.users.find({"course_learn_list.$.class_id": {$in: [classId]}},
{field: {"emails.address": 1}}
).fetch()
Collections
{
"_id": "LMZiLKs2MRhZiiwoS",
"course_learn_list": [
{
"course_id": "M8EiKfxAAzy25WmFH",
"class_id": "jePhNgEuXLM3ZCt98"
},
{
"course_id": "5hbwrfbfxAAzy2nrg",
"class_id": "dfbfnEuXLM3fngndn"
}
],
"emails": [
{
"address": "student1#gmail.com",
"verified": false
}
]
},
{
"_id": "JgfdLKs2MRhZJgfNgk",
"course_learn_list": [
{
"course_id": "M8EiKfxAAzy25WmFH",
"class_id": "jePhNgEuXLM3ZCt98"
},
{
"course_id": "5hbwrfbfxAAzy2nrg",
"class_id": "dfbfnEuXLM3fngndn"
}
],
"emails": [
{
"address": "student2#gmail.com",
"verified": false
}
]
}
I think you want:
Meteor.users.find({ "course_learn_list.class_id": classId },
{ "course_learn_list.$": 1, "emails.address": 1 }).fetch()
This should find the first instance in each course_learn_list array where the classId is your classId.
In this case you probably don't need to use a projection to get the right answer. Here's an example of extracting the verified email addresses using only the . operator in the selector:
const ids = ['jePhNgEuXLM3ZCt98', 'some-other-id'];
const emails =
Meteor.users.find({ 'course_learn_list.class_id': { $in: ids } })
.fetch()
.map(user => _.findWhere(user.emails, { verified: true }).address);
This works for me!
Meteor.publish("getMyClassStudents", function(classId) {
console.log("Publish getMyClassStudents")
var self = this
if (self.userId) {
var data = Meteor.users.find({
"course_learn_list.class_id": classId
}, {
"fields": {
"emails.address": 1
}
})
return data
}
else {
return self.ready()
}
})