I have an object diceRolls that is a IEnumerable>. A data set looks like
{{ 1 , 1 },
{ 1 , 2 },
{ 1 , 3 },
{ 1 , 4 },
{ 1 , 2}}
I need to get a result set that groups the common sets and sums up the total number of rows of that set.
I tried the GroupBy method like this:
var aggregate = sorted.GroupBy(rolls => rolls, rolls => rolls);
But it did not group anything and I could find no fields inside of the rolls object to group by so my only choice was the whole object itself. How do I do this?
You can pass to GroupBy custom IEqualityComparer which implements sequence equals so group by will threat {1, 2} and {1, 2} as the same. But if you know, that your rolls has only 2 elements when you can use anonymous type and its equals implementation:
var sorted = new[] { new[] { 1, 2 }, new[] { 1, 2 }, new[] { 1, 3 }, new[] { 2, 2 } };
var grouped = sorted.GroupBy(roll => new { r0 = roll[0], r1 = roll[1] });
var backToArrays = grouped.Select(g => new[] { g.Key.r0, g.Key.r1 });
This code will produce {{ 1, 2 }, { 1, 3 }, { 2, 2 }}. Is it that you need?
Related
I can hardcode data (labels and series) for a chartist, but need help figuring out how to reformat a db query result.
The app.js contains
const ParmLocation = req.query.ParmLocation
const ParmLine = req.query.ParmLine
console.log("pls2 page requested for " + ParmLocation + " Line " + ParmLine)
// execute a database query
const userToken = db.DBQueryBHBrowser("select PrinterType, count(1) as PCount from printerNames Group by PrinterType");
userToken.then(function(result) {
console.log(JSON.stringify(result.recordset));
res.render('chartpage', {ParmLocation: ParmLocation, ParmLine: ParmLine, S2: result.recordset});
...
The chartpage.js contains below with "data" that works and is in the format needed.
...
var data = {
// A labels array that can contain any sort of values
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
// Our series array that contains series data arrays
series: [[5, 2, 4, 2, 0]]
};
var options = { };
// Create charts with data and options
new Chartist.Line('#TargetRate', data, options);
new Chartist.Line('#SecondShift', {{S2}}, options);
Console log of result.recordset:
[
{ PrinterType: 'Dymo400', PCount: 8 },
{ PrinterType: 'Dymo450', PCount: 30 },
{ PrinterType: 'Dymo4XL', PCount: 13 },
{ PrinterType: 'Laser', PCount: 8 },
{ PrinterType: 'Sato', PCount: 2 }
]
This seemed to work for my purposes
result.recordset.forEach(function(row){
labels.push(Object.values(Object.values(row))[0]);
series.push(Object.values(Object.values(row))[1]);
});
const chartdata = { labels: labels, series: series};
Given a collection with lets say 1.000.000 entries and each of them have their own unique property called number which is indexed. How can I efficiently find the lowest gap in the number sequence.
An easy example would be a sequence of indexes like: 1,2,3,4,6,7,10, where I would like to get back the number 5 since this will be the lowest missing number in the sequence.
Is there a possible way (maybe aggregation) without the need to query all numbers.
One way of doing this would be with a cursor. With a cursor, you can manually iterate through the documents until you find one that matches your criteria.
var cursor = db.coll.find({}).sort({number: 1});
var prev = null
while (cusor.hasNext()) {
var curr = cursor.getNext()
if (prev && prev.number + 1 !== curr.number) break;
prev = curr;
}
One is get all the numbers and find the ones missing between them.
An aggregate example that you can use to not have to get them all. https://www.mongodb.com/community/forums/t/query-to-find-missing-sequence/123771/2
// Assuming the sample data with sequence numbers from 1 thru 10 as follows:
{ id: 1 },
{ id: 2 },
{ id: 4 },
{ id: 7 },
{ id: 9 },
{ id: 10 }
// And, note the missing numbers are 3, 5, 6 and 8. You can use the following aggregation to find them:
db.collection.aggregate([
{
$group: {
_id: null,
nos: { $push: "$id" }
}
},
{
$addFields: {
missing: { $setDifference: [ { $range: [ 1, 11 ] }, "$nos" ] }
}
}
])
I know that it is a bad practice to use skip in order to implement pagination, because when your data gets large skip starts to consume a lot of memory. One way to overcome this trouble is to use natural order by _id field:
//Page 1
db.users.find().limit(pageSize);
//Find the id of the last document in this page
last_id = ...
//Page 2
users = db.users.find({'_id'> last_id}). limit(10);
The problem is - I'm new to mongo and do not know what is the best way to get this very last_id
The concept you are talking about can be called "forward paging". A good reason for that is unlike using .skip() and .limit() modifiers this cannot be used to "go back" to a previous page or indeed "skip" to a specific page. At least not with a great deal of effort to store "seen" or "discovered" pages, so if that type of "links to page" paging is what you want, then you are best off sticking with the .skip() and .limit() approach, despite the performance drawbacks.
If it is a viable option to you to only "move forward", then here is the basic concept:
db.junk.find().limit(3)
{ "_id" : ObjectId("54c03f0c2f63310180151877"), "a" : 1, "b" : 1 }
{ "_id" : ObjectId("54c03f0c2f63310180151878"), "a" : 4, "b" : 4 }
{ "_id" : ObjectId("54c03f0c2f63310180151879"), "a" : 10, "b" : 10 }
Of course that's your first page with a limit of 3 items. Consider that now with code iterating the cursor:
var lastSeen = null;
var cursor = db.junk.find().limit(3);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if (!cursor.hasNext())
lastSeen = doc._id;
}
So that iterates the cursor and does something, and when it is true that the last item in the cursor is reached you store the lastSeen value to the present _id:
ObjectId("54c03f0c2f63310180151879")
In your subsequent iterations you just feed that _id value which you keep ( in session or whatever ) to the query:
var cursor = db.junk.find({ "_id": { "$gt": lastSeen } }).limit(3);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if (!cursor.hasNext())
lastSeen = doc._id;
}
{ "_id" : ObjectId("54c03f0c2f6331018015187a"), "a" : 1, "b" : 1 }
{ "_id" : ObjectId("54c03f0c2f6331018015187b"), "a" : 6, "b" : 6 }
{ "_id" : ObjectId("54c03f0c2f6331018015187c"), "a" : 7, "b" : 7 }
And the process repeats over and over until no more results can be obtained.
That's the basic process for a natural order such as _id. For something else it gets a bit more complex. Consider the following:
{ "_id": 4, "rank": 3 }
{ "_id": 8, "rank": 3 }
{ "_id": 1, "rank": 3 }
{ "_id": 3, "rank": 2 }
To split that into two pages sorted by rank then what you essentially need to know is what you have "already seen" and exclude those results. So looking at a first page:
var lastSeen = null;
var seenIds = [];
var cursor = db.junk.find().sort({ "rank": -1 }).limit(2);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if ( lastSeen != null && doc.rank != lastSeen )
seenIds = [];
seenIds.push(doc._id);
if (!cursor.hasNext() || lastSeen == null)
lastSeen = doc.rank;
}
{ "_id": 4, "rank": 3 }
{ "_id": 8, "rank": 3 }
On the next iteration you want to be less or equal to the lastSeen "rank" score, but also excluding those already seen documents. You do this with the $nin operator:
var cursor = db.junk.find(
{ "_id": { "$nin": seenIds }, "rank": "$lte": lastSeen }
).sort({ "rank": -1 }).limit(2);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if ( lastSeen != null && doc.rank != lastSeen )
seenIds = [];
seenIds.push(doc._id);
if (!cursor.hasNext() || lastSeen == null)
lastSeen = doc.rank;
}
{ "_id": 1, "rank": 3 }
{ "_id": 3, "rank": 2 }
How many "seenIds" you actually hold on to depends on how "granular" your results are where that value is likely to change. In this case you can check if the current "rank" score is not equal to the lastSeen value and discard the present seenIds content so it does not grow to much.
That's the basic concepts of "forward paging" for you to practice and learn.
The simplest way to implement pagination in MongoDB
// Pagination
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 25;
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
query = query.skip(startIndex).limit(limit);
I have a array of objects and each object has two properties:
{key:count}
I am going to configure my chart and I should set the data source of the chart like below:
{meta: "unknown", value: [the count of unknown]},
{meta: "male", value: [the count of male]},
{meta: "female", value: [the count of female]}
Lets say my current array of objects is like:
[{"0":"10"}, {"1":"7"}, {"2":"9"}] in which 0 stands for unknown gender, 1 for male and 2 for female.
How can I set the value in the chart data in one line in such a way that it can find the count of each gender based on its key automatically from the array of objects.
Edit:
I have already written a method and it does the job:
public getKeyValue(data, key) {
for (var i = 0; i < data.length; i++) {
if (data[i].key == key)
return data[i].count;
}
return 0;
}
However, I was wondering if there's a single line code solution like LINQ.
You can, but it's not pretty.
This will do the job:
data.map(item => item.key === key ? item.count : 0).reduce((previous, current) => previous + current);
(Check an example in playground)
But I wouldn't recommend using this instead of your code, because your code won't iterate over all the array elements if one was found to match the criteria, with my solution regardless of whether or not the criteria was met, there will be two iterations over all the array elements.
For example:
var key = "key3",
data: { key: string, count: number}[] = [
{ key: "key1", count: 1 },
{ key: "key2", count: 2 },
{ key: "key3", count: 3 },
{ key: "key4", count: 4 },
{ key: "key5", count: 5 },
//...
{ key: "key1554", count: 1554 }
];
The array (data) has the length of 1554, but you're looking for the 3rd element.
Your way will have 3 iterations and then return the value, my way will have 3108 iterations, one cycle (1554) for the map function and then another cycle for the reduce function.
I wonder whether it's possible to change the order of hash elements based on the orders for values.
for example,
a = { a:3, b:1, c:2}
a = sort_on_values(a)
a = { b:1, c:2, a:3}
It is my understanding that properties on an objects are a set, meaning they have no order, so trying to sort them is not worthwhile.
The particular implementation (node.js) may happen to always return them in insertion order, in which case you are lucky, but I would not rely on that.
If you want an ordered list, then use an ordered list like an array.
For example:
var array = [ { a: 3 }, { b: 1 }, { c: 2 } ];
array.sort(function (a, b) {
return a[Object.keys(a)[0]] - b[Object.keys(b)[0]];
});
console.log(array);
prints out something like
[
{ b: 1 },
{ c: 2 },
{ a: 3 }
]