Dart How to filter a map by keys - flutter

How to handle a map based on the keys?
If in my map the key is equal to my variable then I want to create a list which contains my map
If in my map the key is not equal to my variablecthen I want to create a list which contains 2 map objects with the key which has the nearest lower value and the key which has the nearest higher value
int myVar = 100;
Map values = {
"-900" : 183,
"-800" : 164,
"-700" : 144,
"-600" : 124,
"-500" : 104,
"-400" : 84,
"-300" : 63,
"-200" : 42,
"-100" : 21,
"0" : 0,
"100" : -22,
"200" : -43,
"300" : -64
};
For the exemple with myVar = 100, I want to have this:
int myVar = 100;
Map values = {
"100" : -22,
};
And if myVar = 112 for exemple I need to have the closest keys values. My result must be :
Map values = {
"100" : -22,
"200" : -43,
};
I don't know how to do that.I had perhaps the idea to transform the map into a map list to be able to use the list functions.
List<Map> values = [
{
"arg1" :-900,
"arg2": 183
},
{
"arg1" :-800,
"arg2": 164
},
{
"arg1" :-700,
"arg2": 144
},
// Some other values ...
];
List newValues = values.where((c) => c['arg1'] == 100).toList();
is this the right method? How to transform my basic map if yes ?
EDIT : With the help of #jamesdlin I tried this but I have an error.
import 'dart:collection';
void main() {
int myVar = 100;
Map<int, int> values = {
-900 : 183,
-800 : 164,
-700 : 144,
-600 : 124,
-500 : 104,
-400 : 84,
-300 : 63,
-200 : 42,
-100 : 21,
0 : 0,
100 : -22,
200 : -43,
300 : -64
};
print(values);
Map<int, int> filter(int myVar, SplayTreeMap<int, int> values) {
if (values.containsKey(myVar)) {
return {myVar: values[myVar]};
}
int lowerKey = values.lastKeyBefore(myVar);
int upperKey = values.firstKeyAfter(myVar);
return {
if (lowerKey != null) lowerKey: values[lowerKey],
if (upperKey != null) upperKey: values[upperKey],
};
}
print(filter(myVar, values));
}
I have this on dartpad :
: TypeError: Instance of 'JsLinkedHashMap<int, int>': type 'JsLinkedHashMap<int, int>' is not a subtype of type 'SplayTreeMap<int, int>'Error: TypeError: Instance of 'JsLinkedHashMap<int, int>': type 'JsLinkedHashMap<int, int>' is not a subtype of type 'SplayTreeMap<int, int>'

By default, Map is a LinkedHashMap, where iteration order is key insertion order. For your use, you instead probably will want to use a SplayTreeMap where lookups are O(log n) instead of (ideally) O(1) (WRT the number of elements), but iteration order is in ascending order of keys. This would allow you to use the lastKeyBefore(key) and firstKeyAfter(key) methods to find the previous and next elements if the key isn't directly contained.
I also would recommend that you use int as the key instead of a String. If you use a String, the default ordering will be a lexicographic order ("1" < "10" < "100" < "2"). You could supply you own comparison callback to SplayTreeMap to parse Strings into ints when comparing, but doing so would still require parsing the same Strings multiple times. It'd be much simpler and more efficient to store ints as the keys directly.
Using int keys with a SplayTreeMap, it'd look something like:
import 'package:collection/collection.dart'; // For SplayTreeMap
Map<int, int> filter(int myVar, SplayTreeMap<int, int> values) {
if (values.containsKey(myVar)) {
return {myVar: values[myVar]};
}
int lowerKey = values.lastKeyBefore(myVar);
int upperKey = values.firstKeyAfter(myVar);
return {
if (lowerKey != null) lowerKey: values[lowerKey],
if (upperKey != null) upperKey: values[upperKey],
};
}

Related

Flutter:How to merge two objects and sum the values of the same key?

map1 = { "a": 10, "b": 6 },
map2 = { "a": 10, "b": 6, "c": 7, "d": 8 };
Flutter:How to merge two objects and sum the values of the same key?
Do forEach on the longest map and check if the small map contains the key if it does then update the value with the sum or add the new.
map2.forEach((key, value) {
if (map1.containsKey(key)) {
map1[key] = value + map1[key]!;
} else {
map1[key] = map2[key]!;
}
});
map1 will be the final result.
So, if you want to combine/merge the two maps use this code this answer:
final firstMap = {"1":"2"};
final secondMap = {"2":"3"};
final thirdMap = { // here simple adding element to map
...firstMap,
...secondMap,
};
but if you want to make sum and merge use this :
map2.forEach((k, v) {
if (map1.containsKey(k)) { // check if the map has more then 2 values as the 1st one
map1[k] = v + map1[k]!; // if yes so make the some
} else {
map1[k] = map2[k]!; // if no then add the values to map
}
});
as MEET Prajapati asnwer.

Finding the sum of all values in a List of maps in Flutter

So I have a List of Maps of two dynamic variables.
[{Title: product 1, Item Score: 87.3, Characters: 72, Image Count: 6},
{Title: product 2, Item Score: 85.1, Characters: 56, Image Count: 2}]
How would I add up all of them item scores? There are more items than this in the full file.
I would look at fold in the standard library https://api.dart.dev/stable/1.10.1/dart-core/List/fold.html.
With fold, you iterate over the list of maps, and the first argument is your starting value. In the case of sum, this would be 0. Your inner function in the fold takes 2 arguments, the current accumlation(sum), and the item in your list that is currently being iterated over.
E.g.:
const itemScores = [{'itemScore': 1}, {'itemScore': 2}, {'itemScore': 44}];
var sum = itemScores.fold(0, (i, el){
return i + el['itemScore'];
});
Where i is the current value of the accumulator, and el is the current item in the list being iterated over.
Assuming that, you need to store the value in some variable, you can simply use List.forEach((element){ })
Advantage, you can iterate over as many data as you can, and then store the value
void main(){
var data = [{"Title": "product 1", "Item Score": 87.3, "Characters": 72, "Image Count": 6}, {"Title": "product 2", "Item Score": 85.1, "Characters": 56, "Image Count": 2}];
double totalScores = 0.0;
// looping over data array
data.forEach((item){
//getting the key direectly from the name of the key
totalScores += item["Item Score"];
});
print(totalScores); // OUTPUT ==> 172.39999999999998
}
List arr = [[1,2,3],[4,5,7],[8,9,10],[14,15,19]];
int stairsIn203(List<List<int>> arr) {
// your code here
int _a = 0 ;
List _b = [];
for(List i in arr){
_b += i;
}
_a = _b.reduce((value, element) => value + element) * 20;
return _a;
}
Or
int stairsIn20(List<List<int>> arr) => arr.expand((e) => e).reduce((v, e) => v + e) * 20;
var sum = [{Title: product 1, Item Score: 87.3, Characters: 72, Image
Count: 6}, {Title: product 2, Item Score: 85.1, Characters: 56, Image
Count: 2}]].reduce((item1, item2) => item1["Item Score"] +item2["Item
Score"]);

MongoDB tweet hashtags coincidence count

I have some tweets downloaded to my mongodb.
The tweet document looks something like this:
{
"_id" : NumberLong("542499449474273280"),
"retweeted" : false,
"in_reply_to_status_id_str" : null,
"created_at" : ISODate("2014-12-10T02:02:02Z"),
"hashtags" : [
"Canucks",
"allhabs",
"GoHabsGo"
]
...
}
I want a construct a query/aggregation/map-reduce that will give me the count of tweets that have the same two hash tags. For every pair of nonequal hashtags it gives me the count of tweets eg.:
{'count': 12, 'pair': ['malaria', 'Ebola']}
{'count': 1, 'pair': ['Nintendo', '8bit']}
{'count': 1, 'pair': ['guinea', 'Ebola']}
{'count': 1, 'pair': ['fitness', 'HungerGames']}
...
I've made a python script to do this:
hashtags = set()
tweets = db.tweets.find({}, {'hashtags':1})
#gather all hashtags from every tweet
for t in tweets:
hashtags.update(t['hashtags'])
hashtags = list(hashtags)
hashtag_count = []
for i, h1 in enumerate(hashtags):
for j, h2 in enumerate(hashtags):
if i > j:
count = db.tweets.find({'hashtags' : {'$all':[h1,h2]}}).count()
if count > 0:
pair = {'pair' : [h1, h2], 'count' : count}
print(couple)
db.hashtags_pairs.insert(pair)
But I want to make it just with a query or JS functions to use the map-reduce.
Any ideas?
There's no aggregation pipeline or query that can compute this from your given document structure, so you'll have to use map/reduce if you don't want to drastically change the collection structure or construct a secondary collection. The map/reduce, however, is straightforward: in the map phase, emit a pair (pair of hashtags, 1) for each pair of hashtags in the document, then sum the values for each key in the reduce phase.
var map = function() {
var tags = this.tags;
var k = tags.length;
for (var i = 0; i < k; i++) {
for (var j = 0; j < i; j++) {
if (tags[i] != tags[j]) {
var ts = [tags[i], tags[j]].sort();
emit({ "t0" : ts[0], "t1" : ts[1] }, 1)
}
}
}
}
var reduce = function(key, values) { return Array.sum(values) }

mongodb how to return list of value only from find query

i have a collection placements, each record has fields: placement_id, program_id, category, ...
i need to find all placements what has program_id = 3 or 5 and only return a list of placement_id.
when i tried this command:
db.placements.find({program_id:{$in: [3, 5]}}, {placement_id:1, _id:0})
i got records:
{ "placement_id" : 196 }
{ "placement_id" : 197 }
{ "placement_id" : 198 }
...
is there any way to return just:
[196, 197, 198...]
The cursor from find() is going to yield JSON documents, no matter what.
But you can extract the values you want. Something like this perhaps :
get_placement_id = function(doc) { return doc.placement_id; }
db.placements.find({program_id:{$in: [3, 5]}}, {placement_id:1, _id:0}).map( get_placement_id )
==>
[ 196, 197, 198, ... ]

mongoDb global (scope) variable usage on multi-shards installation

I'm designing system that should be able to process millions of documents and report on them in different ways.
mongoDb map\reduce task is what I'm trying to implement (currently doing some investigation on that).
The very basic document structure is
db.test.insert(
{
"_id" : ObjectId("4f6063601caf46303c36eb27"),
"verbId" : NumberLong(1506281),
"sentences" : [
{
"sId" : NumberLong(2446630),
"sentiment" : 2,
"categories" : [
NumberLong(3257),
NumberLong(3221),
NumberLong(3291)
]
},
{
"sId" : NumberLong(2446631),
"sentiment" : 0,
"categories" : [
NumberLong(2785),
NumberLong(2762),
NumberLong(2928),
NumberLong(2952)
]
},
{
"sId" : NumberLong(2446632),
"sentiment" : 0,
"categories" : [
NumberLong(-2393)
]
},
{
"sId" : NumberLong(2446633),
"sentiment" : 0,
"categories" : [
NumberLong(-2393)
]
}
]
})
So that each document contains sentences, that could belong to different categories.
The report I'm trying to get is number of sentences in category (with percent of verbatims).
I'm doing next map-reduce jobs with finalize method to count different averages.
var map = function() {
var docCategories = new Array();
var catValues = new Array();
for (var i = 0; i < this.sentences.length; i++) { //iterate over sentences.
sentence = this.sentences[i];
for (var j = 0; j < sentence.categories.length; j++) {//iterate over categories
catId= sentence.categories[j].toNumber();
if (docCategories.indexOf(catId) < 0) {
docCategories.push(catId);
catValues.push({sentiment : sentence.sentiment, sentenceCnt: 1});
} else {
categoryIdx = docCategories.indexOf(catId);
catValue = catValues[categoryIdx];
catValue.sentiment = catValue.sentiment + sentence.sentiment;
catValue.sentenceCnt = catValue.sentenceCnt + 1;
}
}
}
totalCount++; //here we do try to count distinctCases see scope.
for (var i = 0; i < docCategories.length; i ++) {
emit(docCategories[i], {count: 1, sentenceCnt: catValues[i].sentenceCnt, sentiment: catValues[i].sentiment, totalCnt : totalCount});
}
};
var reduce = function(key, values) {
var res = {count : 0, sentenceCnt : 0, sentiment : 0};
for ( var i = 0; i < values.length; i ++ ) {
res.count += values[i].count;
res.sentenceCnt += values[i].sentenceCnt;
res.sentiment += values[i].sentiment;
}
return res;
};
var finalize = function(category, values) {
values.sentimentAvg = values.sentiment / values.sentenceCnt;
values.percentOfVerbatim = values.count / totalCount //scope variable (global)
return values;
};
var res = db.runCommand( { mapreduce:'test',
map:map,
reduce:reduce,
out: 'cat_volume',
finalize:finalize,
scope:{totalCount : 0},
});
The most interesting part here is that I'm using totalCount - to count number of verbatims I'm emitting. totalCount is the scope (global) variable.
Everything went well on One mongoDb installation, but when going to a shard instances I'm getting "Infinity" for percentOfVerbatim.
Actually in that case totalCount would be just db.test.count() (number of documents) but in future I'm going to add different conditions for documents to be count.
Doing any other query is very undesirable since db is very heavy.
Are there any other approaches to using global (scope) variables on multi-instance mongodb installation? Or should I use something else?
The scope variables are not shared among the shards. You can treat it as a global constant. Updates to the value won't be visible to map or reduce functions running on different shards.
Finally I've found the way how to count number of documents I'm emitting.
The only way that worked for me is emitting documentId, and puting ids into the array on reduce.
On client side (I'm writing java program) I have to count just all distinct Ids.
So, while doing map I do emit
emit(docCategories[i], {verbIds : [this.verbId.toNumber()], count: 1, sentenceCnt: catValues[i].sentenceCnt, sentiment: catValues[i].sentiment, totalCnt : totalCount});
Reduce function is the following:
var reduce = function(key, values) {
var res = {verbIds : [], count : 0, sentenceCnt : 0, sentiment : 0};
for ( var i = 0; i < values.length; i ++ ) {
// res.verbIds = res.verbIds.concat(values[i].verbIds); //works slow
for ( var j = 0; j < values[i].verbIds.length; j ++ ) {
res.verbIds.push(values[i].verbIds[j]);
}
res.count += values[i].count;
res.sentenceCnt += values[i].sentenceCnt;
res.sentiment += values[i].sentiment;
}
return res;
};
Java side program just count distinct Ids over all of the results.
Actually for 1.1M documents execution slows down significantly