Grouping/Subquery in LINQ to Entities with related ungrouped columns - sql-server

I have this table in DB:
ID | Timestamp | Score
------|---------------------|-------------------
1 | 2013-01-01 12:00:00 | 15
1 | 2013-01-02 11:00:00 | 1
1 | 2013-01-03 16:00:00 | 4
2 | 2013-01-08 04:00:00 | 7
2 | 2013-01-09 08:00:00 | 9
2 | 2013-01-10 11:00:00 | 6
3 | 2013-01-03 12:00:00 | 14
3 | 2013-01-01 10:00:00 | 15
3 | 2013-01-02 00:00:00 | 17
What I need is a last Score for each ID.
In other words, for each ID, I need to pick the row with highest Timestamp and then get the Score from that row.
I managed to do that in SQL server like this (which may not be optimal)
SELECT ID, Timestamp, Score FROM Data cd
LEFT OUTER JOIN
(SELECT ID as SubId, MAX(Timestamp) AS TS
FROM Data GROUP BY ID) AS SUB
ON cd.ID = SUB.SubId
WHERE Timestamp = TS
I need a LINQ to Entities (v4) solution to this situation, but I am unsure how to approach subqueries and grouping in this situation.
#Edit (in reply to comments):
I've tried something like the following, but I don't know how to get the Score from that grouped item, or how to integrate the subquery into this.
from d in Datas
group d by d.ID into group
select new { ID = group.Key, TS = group.Max(i => i.Timestamp) }
Is this scenario possible? Is there any better solution (performance-wise)?

This should achieve what you are wanting:
from d in Datas
group d by d.ID into g
let maxDate = g.Max(d => d.Timestamp)
select new
{
ID = g.Key,
Score = g.First(item => item.Timestamp == maxDate).Score
};
Here's a copy of the complete code listing that I used in LINQPad:
void Main()
{
var testData = GenerateTestData();
var result =
from d in testData
group d by d.ID into g
let maxDate = g.Max(d => d.Timestamp)
select new
{
ID = g.Key,
Score = g.First(item => item.Timestamp == maxDate).Score
};
foreach (var item in result)
{
Console.WriteLine("ID={0}; Score={1}", item.ID, item.Score);
}
}
List<Item> GenerateTestData()
{
List<Item> list = new List<Item>();
list.Add(new Item(1, "2013-01-01 12:00:00", 15));
list.Add(new Item(1, "2013-01-02 11:00:00", 1));
list.Add(new Item(1, "2013-01-03 16:00:00", 4));
list.Add(new Item(2, "2013-01-08 04:00:00", 7));
list.Add(new Item(2, "2013-01-09 08:00:00", 9));
list.Add(new Item(2, "2013-01-10 11:00:00", 6));
list.Add(new Item(3, "2013-01-03 12:00:00", 14));
list.Add(new Item(3, "2013-01-01 10:00:00", 15));
list.Add(new Item(3, "2013-01-02 00:00:00", 17));
return list;
}
class Item
{
public Item(int id, string timestamp, int score)
{
ID = id;
Timestamp = DateTime.Parse(timestamp);
Score = score;
}
public int ID;
public DateTime Timestamp;
public int Score;
}
Which produced the output:
ID=1; Score=4
ID=2; Score=6
ID=3; Score=14

Related

CakePHP 3 - Query builder properly not working for HasMany associations

Table : Services
+-----+--------------+
| id | title |
+-----+--------------+
| 1 | Service 1 |
+-----+--------------+
| 2 | Service 2 |
+-----+--------------+
Table : Workshops { HasMany WorkshopServices }
+-----+---------------+
| id | title |
+-----+---------------+
| 1 | Workshop 1 |
+-----+---------------+
| 2 | Workshop 2 |
+-----+---------------+
Table : WorkshopServices
+-----+--------------+-------------+
| id | workshop_id | service_id |
+-----+--------------+-------------+
| 1 | 1 | 1 |
+-----+--------------+-------------+
| 2 | 1 | 2 |
+-----+--------------+-------------+
I want to find Workshops by service_id
My Query
$this->Workshops->find()
->contain([
'WorkshopServices'
])
->where([
'WorkshopServices.service_id IN'=>[1,2,3]
]);
Query Result
Unknown column `WorkshopServices.service_id` in 'where clause'
Actually Workshops table is not generating any JOIN with WorkshopServices table.
How can I write the query to get proper result from Query Builder?
Use matching:
$array = [1,2,3];
$this->Workshops->find()
->matching('WorkshopServices', function ($q) use($array) {
return $q->where(['WorkshopServices.service_id IN' => $array])
});
I updated #GabrielFerreira's query and Grouping the rows by WorkshopServices.workshop_id, This solution meet my Problem
$array = [1,2,3];
$this->Workshops->find()
->select([
'title'=>'Workshops.title',
'id'=>'Workshops.id',
])
->matching('WorkshopServices', function ($q) use($array) {
return $q->where(['WorkshopServices.service_id IN' => $array]);
})
->group(['WorkshopServices.workshop_id']);

How do I sort and display a React array by year and month?

I'm fairly new to React. Basically I'm trying to display a table of receipts with the following attributes for each receipt:
{
date: '2017-07-03',
description: 'Receipt description,
amount: 300
}
I'm trying to split and order the receipts into sections as follows:
2017
July
03 Jul | Receipt Description | £300.00
------ | ------------------- | -------
01 Jul | Receipt Description | £20.00
May
03 May | Receipt Description | £300.00
------ | ------------------- | -------
01 May | Receipt Description | £20.00
2016
...
I can easily map over the objects and sort the by date but can't figure out how to split them into the year and month sections. Any guidance would be appreciated greatly!
You could do something like that:
var sorted = data.sort(function(a, b) {
return new Date(a.date) - new Date(b.date);
});
var byYearAndByMonth = {};
_.each(sorted, function(item) {
var year = item.date.substring(0,4)
var month = item.date.substring(5,7)
if (typeof byYearAndByMonth[year] === "undefined") {
byYearAndByMonth[year] = {};
}
if (typeof byYearAndByMonth[year][month] === "undefined") {
byYearAndByMonth[year][month] = [];
}
byYearAndByMonth[year][month].push(item);
});
First you sort the array, then you loop over the sorted array and build an object index by year an month.
Then to map over the object in your render() method you'll have to use Object.keys
See this jsfiddle

How to reduce array's numeric values, skipping strings

Using Google Sheets' Script Editor, I need to calculate the average of each column of a table such as this one:
+---------+---------+---------+---------+
| | User 1 | User 2 | User 3 |
+---------+---------+---------+---------+
| | 20/1/17 | 21/1/17 | 22/1/17 |
+---------+---------+---------+---------+
| Movie 1 | 6 | 2 | 0 |
+---------+---------+---------+---------+
| Movie 2 | 2 | 1 | 5 |
+---------+---------+---------+---------+
| Movie 3 | 3 | 1 | 2 |
+---------+---------+---------+---------+
I want the result to be:
+---------+---------+---------+
| User 1 | User 2 | User 3 |
+---------+---------+---------+
| 3.6 | 1.3 | 2.3 |
+---------+---------+---------+
I have this code, which works if I use it on a table composed by numbers only. The problem is my table has strings in it, so the .reduce returns a #NUM! error (Result was not a number).
var sum = array.reduce(function(r, e, i) {
e.forEach(function(a, j) {
r[j] = (r[j] || 0) + a
})
if (i == array.length - 1) r = r.map(function(el) {
return el / array.length
});
return r;
}, []);
return sum;
How can I perform the average with the numeric values only? I tried using .filter, but it didn't seem to work on Google Apps Script Editor, or maybe I was just using it wrong (very likely).
This is the condition I was using to filter:
(typeof el !== 'number')
How about following sample? You can use this on Google Apps Script.
var array = [
[,, 'User 1', 'User 2', 'User 3'],
['Movie 1', '20/1/17', 6.0, 2.0, 5.0],
['Movie 2', '21/1/17', 2.0, 0.0, 2.0],
['Movie 3', '22/1/17', 3.0, 1.0, 3.0]
];
var sum = array.reduce(function(r, e, i) {
e.forEach(function(a, j) {
r[j] = (parseInt(r[j]) || 0) + a;
})
if (i == array.length - 1) r = r.map(function(el) {
return isFinite(el) ? el : "";
});
return r;
}, []);
return [array[0].splice(2, 3), [x for each (x in sum)].splice(2, 3)];
>>> [[User 1, User 2, User 3], [11.0, 3.0, 10.0]]

ng-options get referenced document and display its field as value for dropdown population

I'm working with meteor+angular. The following loops through all the documents in the items collection. Within the loop, once a meal == true is hit, it loops the related combos to make dropdowns that use as options, documents from the comboitems collection.
// controller.ng.js
angular.module("myApp").controller("menuCtrl",
function($scope, $stateParams, $meteor){
$scope.items = $meteor.collection(Items);
$scope.combos = $meteor.collection(Combos);
$scope.comboitems = $meteor.collection(Comboitems);
}
);
// view.ng.html
<div ng-repeat="item in items">
<div ng-if="true === item.meal">
<ul>
<li ng-repeat="combo in combos | filter: {itemId: item._id}">
<select ng-model="comboitems" ng-options="comboitem.itemId for comboitem in (comboitems | filter: {comboId: combo._id})"></select>
</li>
</ul>
</div>
</div>
Using the data provided below, once I hit Pizza Meal I will have two dropdowns: Choose your pizza and Choose your drink, where in the former I will be able to choose between pizza 1 and pizza 2, while in the latter between drink 1 and drink 2.
This is the result
My issue is that I can't find a way to render the item.name, knowing its id in the comboitems collection once the <select> is being rendered.
Here is a sample data showing how the relations work:
items collection:
_id | name | price | meal
---------------------------------
a | pizza 1 | 5.00 | false
b | pizza 2 | 6.00 | false
c | drink 1 | 1.00 | false
d | drink 2 | 1.00 | false
e | Pizza meal | 5.00 | true
combos collection:
_id | name | itemId
-----------------------------------
x | Choose your pizza | e
y | Choose your drink | e
comboitems collection:
_id | itemId | comboId
-----------------------
1 | a | x
2 | b | x
3 | c | y
3 | d | y
Use select as in your ng-options directive:
<select ng-model="selectesdItem"
ng-options="comboitem.itemId as getName(comboitem.itemId) for comboitem in (comboitems | filter: {comboId: combo._id})">
</select>
and bind the result to a function that will return the name:
$scope.getName = function(id) {
return $filter('filter')($scope.items, { _id: id }, true)[0].name;
}
http://plnkr.co/edit/efym7LpeSjIqkDKbdLR0?p=preview

Is it possible to optimize this code?

I have to display a TreeView in a WinForm, based on an complex Dictionary and I'm looking for the 'shortest' method to do it. I think it can be done in one LINQ query but I don't know how and I'm not even sure if it is possible.
Here an example of the entry dictionary :
Dictionary<String, String> dict = new Dictionary<String, String>()
{
{"aaa.bbb.ccc.ddd", "value1"},
{"aaa.bbb.ccc.eee", "value2"},
{"aaa.bbb.fff.ggg", "value3"},
{"aaa.hhh.iii.jjj", "value4"},
{"bbb.ddd", "value5"},
{"ccc", "value6"}
};
And I want to get back a TreeView like this :
|--+ aaa
| |--+ bbb
| | |--+ ccc
| | | |--- ddd = value1
| | | |--- eee = value2
| | |
| | |--+ fff
| | | |--- ggg = value3
| |
| |--+ hhh
| | |--+ iii
| | | |--- jjj = value4
|
|--+ bbb
| |--- ddd = value5
|
|--+ ccc = value6
And here what I have got now (I don't handle the value yet) :
List<String> list = new List<string>() {
"aaa.bbb.ccc.ddd",
"aaa.bbb.ccc.eee",
"aaa.bbb.fff.ddd",
"aaa.bbb.fff.ggg",
"aaa.ggg.fff.hhh",
"hhh.iii.jjj.kkk"
};
Action<TreeNode, String> traverse = null;
traverse = (node, chaine) =>
{
String[] tab = chaine.Split(new char[] { '.' }, 2);
TreeNode child = null;
if (node.Nodes.ContainsKey(tab[0]))
{
child = node.Nodes[tab[0]];
}
else
{
child = node.Nodes.Add(tab[0]); // + ((tab.Length > 1) ? " - " + tab[1] : ""));
child.Name = tab[0];
}
if (tab.Length > 1 && !String.IsNullOrEmpty(tab[1]))
traverse(child, tab[1]);
};
TreeNode test = this.treeView1.Nodes.Add("test");
list.ForEach(x => traverse(test, x));
I hope I'm clear enough in my explanation.
There is a fair bit of logic going on in your Action, so I doubt it can be done within a single LINQ query so that it would look something like
var query = from this in that
where this is t
select this
But what you could do is rewrite it a little bit, something like:
public void AnotherWay()
{
TreeNode parent = this.treeView1.Nodes.Add("test");
List<String> list = new List<String>()
{
"aaa.bbb.ccc.ddd",
"aaa.bbb.ccc.eee",
"aaa.bbb.fff.ddd",
"aaa.bbb.fff.ggg",
"aaa.ggg.fff.hhh",
"hhh.iii.jjj.kkk"
};
list.ForEach(x =>
{
TreeNode root = parent;
TreeNode child = null;
x.Split(new[] { '.' })
.ToList()
.ForEach(i =>
{
child = root.Nodes.ContainsKey(i) ?
root.Nodes[i] :
root.Nodes.Add(i);
child.Name = i;
root = child;
});
});
}
This code does exactly what you already posted, but is just smaller and IMO reads slightly clearer.
Generally I see an Action or Func that performs a fair bit of logic to be a code smell, especially if it is reused several times (in which case it should be extracted out into it's own method, but I digress).
In your case though it appears that the Action is only being used the once in the line list.ForEach(x => traverse(test, x)); and so the functionality can simply replace your call to the Action, as in my example above.
(However, if the complexity of the logic or the LOC increased then in this example I would be tempted to move the functionality out into it's own method for maintainability).
This approach also allows you to very easily cater for your second requirement of handling dictionary's with little modification, such as:
public void DictFunc()
{
TreeNode parent = this.treeView1.Nodes.Add("test");
Dictionary<String, String> dict = new Dictionary<String, String>()
{
{ "aaa.bbb.ccc.ddd", "Value1" },
{ "aaa.bbb.ccc.eee", "Value2" },
{ "aaa.bbb.fff.ddd", "Value3" },
{ "aaa.bbb.fff.ggg", "Value4" },
{ "aaa.ggg.fff.hhh", "Value5" },
{ "hhh.iii.jjj.kkk", "Value6" }
};
dict.ToList().ForEach(x =>
{
// For brevity, same as logic in example above.
// Plus the small amount (~three LOC) of additional logic
// required to handle the values.
});
}
I've left the dictionary value handling as an exercise for you, but the rest of the logic inside the ForEach is identical to that in my first example.

Resources