Rx GroupBy: Remove item or Update exising item not trigger regroup - system.reactive

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);
}

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];

Sorting order of teams in league table by direct matches between teams in dart & 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

How to sort all the data by date in google apps script?

I have retrieved the data from REST API and inserted into google apps script but i am not sure how to sort the data based on the subscription date. I have used the sort() but it is only sorting one column instead of everything. This is my current code and screenshot so far:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mainSheet = ss.getSheetByName("test")
var apiKey = 'test';
var URL_STRING = "";
var url = URL_STRING + "?ApiKey=" + apiKey;
var response = UrlFetchApp.fetch(url);
var json = response.getContentText();
var data = JSON.parse(json);
var firstname = [];
var lastname = [];
var subscriptionDate = [];
for (var i=0;i<data.output.o1.length;i++){
fn=(data.output.o1[i].first_name);
firstname.push([fn]);
ln=(data.output.o1[i].last_name);
lastname.push([ln]);
sd=(data.output.o1[i].subscription_date);
subscriptionDate.push([sd]);
};
mainSheet.getRange(2, 1, firstname.length).setValues(firstname);
mainSheet.getRange(2, 2, lastname.length).setValues(lastname);
mainSheet.getRange(2, 3, subscriptionDate.length).setValues(subscriptionDate);
}
In this case, how about sorting the values when the array for putting to Spreadsheet is created? When this is reflected to your script, it becomes as follows.
From:
var firstname = [];
var lastname = [];
var subscriptionDate = [];
for (var i=0;i<data.output.o1.length;i++){
fn=(data.output.o1[i].first_name);
firstname.push([fn]);
ln=(data.output.o1[i].last_name);
lastname.push([ln]);
sd=(data.output.o1[i].subscription_date);
subscriptionDate.push([sd]);
};
mainSheet.getRange(2, 1, firstname.length).setValues(firstname);
mainSheet.getRange(2, 2, lastname.length).setValues(lastname);
mainSheet.getRange(2, 3, subscriptionDate.length).setValues(subscriptionDate);
To:
var values = data.output.o1.map(({first_name, last_name, subscription_date}) => [first_name, last_name, subscription_date]);
values.sort((a, b) => new Date(a[2]).getTime() > new Date(b[2]).getTime() ? 1 : -1);
mainSheet.getRange(2, 1, values.length, values[0].length).setValues(values);
When values.sort((a, b) => new Date(a[2]).getTime() > new Date(b[2]).getTime() ? 1 : -1); is modified to values.sort((a, b) => new Date(a[2]).getTime() > new Date(b[2]).getTime() ? -1 : 1);, the order of sort direction is changed.
Note:
As other method, when the following script to the bottom of your current script, the sort is run with the column "C".
mainSheet.getRange("A2:C").sort([{ column: 3, ascending: true }]);
In this answer, from your sample image, I supposed that your column "C" is the date object.
References:
map()
sort()
sort(sortSpecObj)
mainSheet.getRange(2,1,mainSheet.getLastRow()-1,3).sort({column:3,ascending:true});

sort 2 dependent dart arrays

I have got 2 arrays
ids=[GEORGE, RUSTY, RIYAZAT, JAMES PAWNED];
avgscore=[10, 13, 3, 40];
I want the result array to be sorted in descending order wrt marks scored
and both the arrays should be sorted accordingly, the result should be like this
ids should be sorted as [40, 13, 10, 3];
avgscore should be sorted as [JAMES PAWNED, RUSTY, GEORGE, RIYAZAT];
You should make a class which binds your two data together. Then it is easy to sort based on the avgscore value:
class Person {
final String id;
final int avgscore;
Person(this.id, this.avgscore);
#override
String toString() => '$id = $avgscore';
}
void main() {
var ids = ['GEORGE', 'RUSTY', 'RIYAZAT', 'JAMES PAWNED'];
var avgscore = [10, 13, 3, 40];
final persons = List.generate(ids.length, (i) => Person(ids[i], avgscore[i]));
print(persons); // [GEORGE = 10, RUSTY = 13, RIYAZAT = 3, JAMES PAWNED = 40]
persons.sort((p1, p2) => p2.avgscore.compareTo(p1.avgscore));
print(persons); // [JAMES PAWNED = 40, RUSTY = 13, GEORGE = 10, RIYAZAT = 3]
// If you need to split the values into two arrays again
ids = persons.map((p) => p.id).toList();
avgscore = persons.map((p) => p.avgscore).toList();
print(ids); // [JAMES PAWNED, RUSTY, GEORGE, RIYAZAT]
print(avgscore); // [40, 13, 10, 3]
}
personally I think that it would be easier for you if you work with a list of Maps specially if you're planning to have more data and not just 4 in your list
here's an example of what working with a map would look like
void sortMap() {
var temp= {
"GEORGE":10,
"RUSTY":13,
"RIYAZAT":3,
"JAMES PAWNED":40
};
temp = SplayTreeMap.from(
temp, (key1, key2) => temp[key1].compareTo(temp[key2]));
print(temp);
}

Linq join query

For example
DB with 2 Tables
Book [BookId (int), Title (nvarchar), ShowInWebshop (bit)] and
InventoryDetail [InventoryDetailId (int), BookId (int), Quantity (int)]
Execute: SELECT * FROM Books LEFT JOIN InventoryDetails ON books.BookId = InventoryDetails.BookId
The output shows all Book columns and related InventoryDetails columns (including the InventoryDetails.BookId column)
..so far so good ...
Trying to transform this query into a Linq one (using LinqPad, comparing several examples, common sense, etc.) I compilated the following generic List (because I wanted a present a list)
private List<Book> Books(int count){
var books = webshopDB.Books
.Join<Book, InventoryDetail, int, Book>( webshopDB.InventoryDetails,
b => b.BookId,
i => i.BookId,
(b, i) => b )
.Where(b => b.ShowInWebshop == true)
.Take(count)
.ToList();
return books
}
This module returns a list of books! Not the one I expected, though! It returns only book details such as Title and ShowOnSite NOT the details from the InventoryDetail table: Quantity
What do I forget?
The result how it works so far ...
public ActionResult Index()
{
// This return a list of tuples {(WebshopDB.Models.Book, WebshopDB.Models.InventoryDetail)}
// Each tuple containing two items:
// > Item1 {WebshopDB.Models.Book}
// > Item2 {WebshopDB.Models.InventoryDetail}
var tuple_books = ListOfTuples_BookInventoryDetail(5);
...
// next step(s)
// add a ViewModel viewmodel
// ...
return (viewmodel);
}
private List<Tuple<Book, InventoryDetail>> ListOfTuples_BookInventoryDetail(int count)
{
var list_of_tuples = new List<Tuple<Book, InventoryDetail>>();
var showbooks = webshopDB.Books
.Join(webshopDB.InventoryDetails, b => b.BookId, i => i.BookId, (b, i) => new { b = b, i = i })
.Where(o => (o.b.ShowInWebshop == true))
.Where(o => o.b.BookThumbUrl.Contains(".jpg"))
.OrderByDescending(o => o.b.OrderDetails.Count())
.Take(count);
foreach (var item in showbooks)
{
list_of_tuples.Add( Tuple.Create<Book, InventoryDetail>( (item.b), (item.i) ) );
}
return list_of_tuples;
}
You need to select from both tables, e.g.
from b in webshop.Books
from i in webshopDB.InventoryDetails
where i.BookId = b.BookId
select b.BookId, b.Title, b.ShowInWebshop, i.InventoryDetailId, i.Quantity
You're getting books because you specified that in your Join statement with the final selector of => b. You want to select both, so use this:
var query = webshopDB.Books.Join(webshopDB.InventoryDetails,
b => b.BookId, i => i.BookId,
(b, i) => new { Book = b, InventoryDetail = i });
Then when you iterate over the results you can use:
foreach (var item in query)
{
Console.WriteLine(item.Book.SomeProperty);
Console.WriteLine(item.InventoryDetail.SomeProperty);
}
The other problem with your method is that the return type is a List<Book>. So the above won't work since a Book class is separate from an InventoryDetail class. You need to setup a new class to encompass both, or use a Tuple<Book, InventoryDetail> if using .NET 4.0.
To return a particular property you could modify the statement to:
var query = webshopDB.Books.Join(webshopDB.InventoryDetails,
b => b.BookId, i => i.BookId,
(b, i) => new { b.BookId, i.Quantity });
Again, you need an appropriate return type if you're returning a List<T>.
EDIT: to get a Dictionary<Book, InventoryDetail> you could use the earlier query as follows:
var query = webshopDB.Books.Join(webshopDB.InventoryDetails,
b => b.BookId, i => i.BookId,
(b, i) => new { Book = b, InventoryDetail = i })
.ToDictionary(o => o.Book, o => o.InventoryDetail);
Of course you can use the Where and Take as necessary before the ToDictionary call. You can also project just the properties you need as the query just before this one. You need to project it into an anonymous type via the new keyword (it was missing earlier so take another look at it).
Quering the answer of SoftwareRockstar in LinqPad4 generates errors! So, that's not the way!
//This does works (in LinqPad4)
from b in webshopDB.Books
from i in webshopDB.InventoryDetails
where b.BookId == i.BookId
where b.ShowInWebshop == true
select new { b.Title, i.Quantity, b.ShowInWebshop }