Sorting order of teams in league table by direct matches between teams in dart & flutter - flutter

I am creating league table simulator (4 teams in table) - user can put scores and table is updating (i use provider to get data from user).
I want sort order of teams in table by few factors:
number of points
goals difference
goals scored
direct matches between teams who have this same values in earlier factors.
For first three i have sorting method but how to compare direct matches between teams? It is not one value like in the other keys and it depends on which teams will we compare. Is there any rational way for it?
const keys = ['points', 'goalsdifference', 'goalsfor'];
final mapsort = Map.fromEntries(
points.entries.toList()
..sort(
(b, a) {
int index = 0;
String currentKey = keys[index];
while (a.value[currentKey]!.compareTo(b.value[currentKey]!) == 0 &&
index < keys.length - 1) {
++index;
currentKey = keys[index];
}
return a.value[currentKey]!.compareTo(b.value[currentKey]!);
},
),
);
Map where that data is:
Map<dynamic, dynamic>? points = {
'Team1': {
'points': simulator.data.team1points,
'goalsfor': simulator.data.team1goalsfor,
'goalsagainst': simulator.data.team1goalsagainst,
'goalsdifference': simulator.data.team1goalsdifference
},
'Team2': {
'points': simulator.data.team2points,
'goalsfor': simulator.data.team2goalsfor,
'goalsagainst': simulator.data.team2goalsagainst,
'goalsdifference': simulator.data.team2ogoalsdifference
},
'Team3': {
'points': simulator.data.team3points,
'goalsfor': simulator.data.team3goalsfor,
'goalsagainst': simulator.data.team3goalsagainst,
'goalsdifference': simulator.data.team3goalsdifference
},
'Team4': {
'points': simulator.data.team4points,
'goalsfor': simulator.data.team4goalsfor,
'goalsagainst': simulator.data.team4goalsagainst,
'goalsdifference':simulator.data.team4goalsdifference
},
};
This is how it looks in my class for team number 1 (this same is for another teams):
int scoreEqualityValue(left, right) => left == right ? 1 : 0;
int scoreBetterValue(left, right) => left > right ? 3 : 0;
team1match1 = team1match1 //this is number of goals for team1 in first match
team1match2 = team1match2
team1match3 = team1match3
team1points = scoreBetterValue(team1match1, team2match1) + scoreEqualityValue(team1match1, team2match1) + scoreBetterValue(team1match2, team3match2) + scoreEqualityValue(team1match2, team3match2) + scoreBetterValue(team1match3, team4match3) + scoreEqualityValue(team1match3, team4match3)
team1goalsfor = team1match1 + team1match2 + team1match3
team1goalsagainst = team2match1 + team3match2 + team4match3
team1goalsdifference = team1match1 + team1match2 + team1match3 - team2match1 - team3match2 - team4match3

Related

Group By with Entity Framework

enter image description hereI have a code. And there you need to make a grouping by name.
//<date,<partid,amount>>
Dictionary<string, Dictionary<int, double>> emSpending = new Dictionary<string, Dictionary<int, double>>();
foreach (Orders order in db.Orders.ToList())
{
foreach (OrderItems orderitem in order.OrderItems.ToList())
{
if (!emSpending.ContainsKey(order.Date.ToString("yyyy-MM"))) emSpending.Add(order.Date.ToString("yyyy-MM"), new Dictionary<int, double>());
if (!emSpending[order.Date.ToString("yyyy-MM")].ContainsKey(Convert.ToInt32(orderitem.PartID))) emSpending[order.Date.ToString("yyyy-MM")].Add(Convert.ToInt32(orderitem.PartID), 0);
emSpending[order.Date.ToString("yyyy-MM")][Convert.ToInt32(orderitem.PartID)] += Convert.ToDouble(orderitem.Amount);
}
}
DataGridViewColumn col1 = new DataGridViewColumn();
col1.CellTemplate = new DataGridViewTextBoxCell();
col1.Name = "Department";
col1.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
col1.HeaderText = "Department";
dgvEMSpending.Columns.Add(col1);
foreach (string date in emSpending.Keys)
{
DataGridViewColumn col = new DataGridViewColumn();
col.Name = date;
col.HeaderText = date;
col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
col.CellTemplate = new DataGridViewTextBoxCell();
dgvEMSpending.Columns.Add(col);
}
List<string> allKey = emSpending.Keys.ToList();
foreach (string date in allKey)
if (date == "Department") continue;
else
{
dgvEMSpending.Rows.Add();
foreach (int partid in emSpending[date].Keys)
{
dgvEMSpending.Rows[dgvEMSpending.Rows.Count - 1].Cells[0].Value = db.Parts.Where(x => x.ID == partid).SingleOrDefault().Name.GroupBy(Name);
for (int i = 1; i < dgvEMSpending.Columns.Count; i++)
{
if (!emSpending.ContainsKey(dgvEMSpending.Columns[i].Name)) emSpending.Add(dgvEMSpending.Columns[i].Name, new Dictionary<int, double>());
if (!emSpending[dgvEMSpending.Columns[i].Name].ContainsKey(partid)) emSpending[dgvEMSpending.Columns[i].Name].Add(partid, 0);
double val = emSpending[dgvEMSpending.Columns[i].Name][partid];
dgvEMSpending.Rows[dgvEMSpending.RowCount - 1].Cells[i].Value = val;
}
}
}
I tried to use group by myself, but something doesn't work. It just outputs the same names, and I want to group them so that there is a grouping. Pls helped to me.
Ok, a few issues to help you out first. This code:
foreach (Orders order in db.Orders.ToList())
{
foreach (OrderItems orderitem in order.OrderItems.ToList())
{
if (!emSpending.ContainsKey(order.Date.ToString("yyyy-MM"))) emSpending.Add(order.Date.ToString("yyyy-MM"), new Dictionary<int, double>());
if (!emSpending[order.Date.ToString("yyyy-MM")].ContainsKey(Convert.ToInt32(orderitem.PartID))) emSpending[order.Date.ToString("yyyy-MM")].Add(Convert.ToInt32(orderitem.PartID), 0);
emSpending[order.Date.ToString("yyyy-MM")][Convert.ToInt32(orderitem.PartID)] += Convert.ToDouble(orderitem.Amount);
}
}
Right off the bat this is going to trip lazy loading on OrderItems. If you have 10 orders 1-10 you're going to be running 11 queries against the database:
SELECT * FROM Orders;
SELECT * FROM OrderItems WHERE OrderId = 1;
SELECT * FROM OrderItems WHERE OrderId = 2;
// ...
SELECT * FROM OrderItems WHERE OrderId = 10;
Now if you have 100 orders or 1000 orders, you should see the problem. At a minimum ensure that if you are touching a collection or reference on entities you are loading, eager load it with Include:
foreach (Orders order in db.Orders.Include(x => x.OrderItems).ToList())
This will run a single query that fetches the Orders and their OrderItems. However, if you have a LOT of rows this is going to take a while and consume a LOT of memory.
The next tip is "only load what you need". You need 1 field from Order and 2 fields from OrderItem. So why load everything from both tables??
var orderItemDetails = db.Orders
.SelectMany(o => o.OrderItems.Select(oi => new { o.Date, oi.PartId, oi.Amount })
.ToList();
This would give us just the Order date, and each Part ID and Amount. Now that this data is in memory we can group it to populate your desired dictionary structure without having to iterate over it row by row.
var emSpending = orderItemDetails.GroupBy(x => x.Date.ToString("yyyy-MM"))
.ToDictionary(g => g.Key,
g => g.GroupBy(y => y.PartId)
.ToDictionary(g2 => g2.Key, g2 => g2.Sum(z => z.Amount)));
Depending on the Types in your entities you may need to insert casts. This first groups the outer dictionary of the yyyy-MM of the order dates, then it groups the remaining data for each date by part ID, and sums the Amount.
Now relating to your question, from your code example I'm guessing the problem area you are facing is this line:
dgvEMSpending.Rows[dgvEMSpending.Rows.Count - 1].Cells[0].Value = db.Parts
.Where(x => x.ID == partid)
.SingleOrDefault().Name.GroupBy(Name);
Now the question would be to explain what exactly you are expecting from this? You are fetching a single Part by ID. How would you expect this to be "grouped"?
If you want to display the Part name instead of the PartId then I believe you would just want to Select the Part Name:
dgvEMSpending.Rows[dgvEMSpending.Rows.Count - 1].Cells[0].Value = db.Parts
.Where(x => x.ID == partid)
.Select(x => x.Name)
.SingleOrDefault();
We can go one better to fetch the Part names for each used product in one hit using our loaded order details:
var partIds = orderItemDetails
.Select(x=> x.PartId)
.Distinct()
.ToList();
var partDetails = db.Parts
.Where(x => partIds.Contains(x.ID))
.ToDictionary(x => x.ID, x => x.Name);
This fetches us a dictionary set indexed by ID for the part names, it would be done outside of the loop after we had loaded the orderItemDetails. Now we don't have to go to the DB with every row:
dgvEMSpending.Rows[dgvEMSpending.Rows.Count - 1].Cells[0].Value = partDetails[partId];

Rx GroupBy: Remove item or Update exising item not trigger regroup

private static void TestGroupBy()
{
var rxList = new ReactiveList<int>();
Observable.Merge(rxList.ItemsAdded, rxList.ItemChanged.Select(x => x.Sender) ).GroupBy(i => i%3)
.Subscribe(group =>
{
Console.WriteLine(string.Format("--> Group {0} is created", group.Key) );
int child = 0;
group.Subscribe(c =>
{
Console.WriteLine(string.Format(" ------> Adding child {0} to Group {1} - ChildCnt: {2}", c, group.Key, ++child) );
});
});
Console.WriteLine("Add 1 to 10... ");
rxList.AddRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
Console.WriteLine("Change item 0 to 11, expect to be moved from group 0, to Group 2 ");
rxList[0] = 11;
Console.WriteLine("Remove item at 0, expect the item to be removed from the group ...");
rxList.RemoveAt(0);
}
I have this piece of code here. I would like the regroup to trigger if item is either removed, or item is replaced with new one. How do I achieve this?
I'm not sure if it's possible to implement item removing using groupBy like you want to do. Take a look at this picture: it seems like you want to alter the elements that have already been pushed to one of the "grouped" streams.
In your case, I would simply create several derived collections (as many as you have groups), with different selectors:
public void Test()
{
var source = new ReactiveList<int>();
var derived1 = source.CreateDerivedCollection(x => x, filter: x => x%3 == 0);
var derived2 = source.CreateDerivedCollection(x => x, filter: x => x%3 == 1);
var derived3 = source.CreateDerivedCollection(x => x, filter: x => x%3 == 2);
}

Maximizing Performance with the Entity Framework [duplicate]

This question already has answers here:
How to "warm-up" Entity Framework? When does it get "cold"?
(5 answers)
Closed 7 years ago.
I am developing travel web site.
When user input a location for search(autocomplete) my action return all cities, that cities regions, regions, regions translations, hotels..... which start with user input
I used Entity code first. But it is response time is too much. How can I optimize this? How can I decrease time?
public JsonResult AutoComplateCityxxxxxxxx(string culture, string q)
{
List<Tuple<string, int, int>> result = new List<Tuple<string, int, int>>();
using (var db = new TourismContext())
{
ModelState.Remove(q);
var query = SearchWordFunctions.WordFunctions(q);
var ListCity = db.CityTranslations.Where(
c => (c.Slug.StartsWith(query) || c.Name.StartsWith(query))
&&
c.City.Latitude.HasValue
).GroupBy(x => x.CityID).Select(g => g.FirstOrDefault()).Take(10);
var ListRegion = db.RegionTranslations.Where(
r => (r.Slug.StartsWith(query) || r.Name.StartsWith(query))
&&
r.Region.Latitude.HasValue
&&
r.Region.RefID == 0 && r.Region.IsShow > 0
).GroupBy(x => x.RegionID).Select(g => g.FirstOrDefault()).Take(10);
var LandMark = db.CityLandMarks.Where(l => l.Translations.Any(t => t.Name.StartsWith(query)) && l.Latitude.HasValue).Take(10);
var hotel = db.HotelTranslations.Where(t => t.Url.Contains(query) && t.Hotel.Status > 0 && t.Culture.Code == culture).ToList();
result.Clear();
foreach (var item in ListCity.OrderBy(o => o.Name.Length))
{
result.Add(new Tuple<string, int, int>(string.Concat(item.Name, " - <b>", item.City.Country.Translations.Single(t => t.CultureID == 1).Name, "<b>"), item.CityID, 1));
if (db.Regions.Any(r => r.CityID == item.CityID))
{
var regions = db.Regions.Where(r => r.CityID == item.CityID && r.Latitude.HasValue && r.RefID == 0 && r.IsShow > 0).GroupBy(g => g.ID).Select(x => x.FirstOrDefault()).ToList().OrderByDescending(o => o.SearchRating).Take(10);
foreach (var regItem in regions)
{
result.Add(new Tuple<string, int, int>(string.Concat(regItem.Translations.FirstOrDefault().Name, " - <b>", item.Name, "</b> - <b>", regItem.City.Country.Translations.FirstOrDefault().Name, "<b>"), regItem.ID, 2));
}
}
}
if (ListCity.Count() <= 0)
{
foreach (var item in ListRegion)
{
result.Add(new Tuple<string, int, int>(string.Concat(item.Name, " - <b>", item.Region.City.Translations.Single(t => t.Culture.Code == culture).Name, "</b> - <b>", item.Region.City.Country.Translations.Single(t => t.Culture.Code == culture).Name, "</b>"), item.RegionID, 2));
}
}
foreach (var item in LandMark)
{
result.Add(new Tuple<string, int, int>(string.Concat(item.Translations.FirstOrDefault().Name, " - <b>", item.City.Translations.FirstOrDefault().Name, "</b> - <b>", item.City.Country.Translations.FirstOrDefault().Name, "</b>"), item.ID, 3));
}
foreach (var item in hotel)
{
result.Add(new Tuple<string, int, int>(string.Concat(item.Name, " - <b class=\"refid\" data=\"" + item.HotelID + "\">", item.Hotel.Region.City.Translations.First().Name, "</b>"), item.Hotel.Region.CityID, 1));
}
}
return Json(result, JsonRequestBehavior.AllowGet);
}
Without seeing your generated DB schema or knowing anything about the DB engine or server configuration, it is difficult to say with any certainty what will improve your query performance the most. Reviewing your code, though, I would recommend ensuring that the following attributes have indexes associated with them:
CityTranslations.Slug
CityTranslations.Name
RegionTranslations.Slug
RegionTranslations.Name
CityLandmarks.Name
That should give you an immediate boost, since StartsWith should generate a clause in the form LIKE 'xxx%', so an index should significantly improve performance.
HotelTranslations may need to be revisited to some extent, since Contains will generate a clause of the form LIKE '%xxx%' which will not benefit from a simple index.
If there are already indexes on those fields, then please provide additional information about your configuration (DB, server config, generated schema, etc).

Best way to order by children collection with Entity

I am seeking for the best solution for this simple problem.
Run in C#/Entity the following SQL:
select user.name, userstat.point from user, userstat where userstat.user_id = user.id order by userstat.point desc
There is a User table [Id, Name, ...] and Statistic table [Id, UserId, Point. ...], where it's connected to User by Statistic.UserId. It's a 1:1 relation, so there is (max) 1 Statistic record for each User record.
I want to have a list User+Point, ordered by Point desc, and select a range, let's say 1000-1100.
Currently I have this:
public List<PointItem> Get(int startPos, int count)
{
using (DB.Database db = new DB.Database())
{
var dbList = db.Users.Where(user => .... ).ToList();
List<PointItem> temp = new List<PointItem>(count);
foreach (DB.User user in db.Users)
{
//should be always 1 stat for the user, but just to be sure check it...
if (user.Stats != null && user.Stats.Count > 0)
temp.Add(new PointItem { Name = user.Name, Point = user.Stats.First().Point });
} <--- this foreach takes forever
return temp.OrderByDescending(item => item.Point).Skip(startPos).Take(count).ToList();
}
}
It works fine, but when I have 10000 User (with 10000 UserStat) it runs for 100sec, which is only 1000x slower than I want it to be.
Is there more efficient solution than this?
If I run SQL, it takes 0 sec basically for 10K record.
EDIT
I made it faster, now 100sec -> 1 sec, but still I want it faster (if possible).
var userPoint = db.Users
.Where(u => u.UserStats.Count > 0 && ....)
.Select(up => new
{
User = up,
Point = up.UserStats.FirstOrDefault().Point
})
.OrderByDescending(up => up.Point)
.ToList();
var region = userPoint.Skip(0).Take(100);
Ok, I found the solution, the following code is 0.05 sec. Just need to go from child to parent:
using (DB.Database db = new DB.Database())
{
var userPoint = db.UserStats
.Where(s => s.User.xxx .....)
.Select(userpoint => new
{
User = userpoint.User.Name,
Point = userpoint.Point
})
.OrderByDescending(userpoint => userpoint.Point)
.ToList().Skip(startPos).Take(count);
}

Excluding child records from entity objects c#

I am having an issue with writing a query to exclude records from entity framework child objects.
My query
var response = db.USER_PROFILE.Where(x =>
x.IPAD_SERIAL_NUMBER == id
&& x.ACTIVE_FLAG == 1
&& x.USER_BRAND.Any(y => y.ACTIVE_FLAG == 1)
).FirstOrDefault();
Returned result
One USER_PROFILE object with
Two USER_BRAND objects
USER_BRAND - ACTIVE_FLAG = 1
USER_BRAND - ACTIVE_FLAG = 0
I don't want to return a record with ACTIVE_FLAG = 0 in the collection.
How do I do easily that?
Thanks in advance!
I was able to do it this way
var query = db.USER_PROFILE
.Select(x=> new
{
User = x,
UserBrands = x.USER_BRAND.Where(y=> y.ACTIVE_FLAG == 1)
.Select(a=> new
{
UserBrand = a,
Brand = a.BRAND
}),
});
var filtered = query.Select(x=> x.User);