Sorting by nested Dictionary value? - c#

var userInformation = new Dictionary<string, Dictionary<string, int>>();
I need a new dictionary that equals this one, but is sorted first by key then by the value's value. I tried:
var resultInformation = userInformation.OrderBy(k => k.Key).ThenBy(v => v.Value.OrderByDescending(x => x.Value));
I tried a couple of other methods but no effect.

Dictionaries aren't sorted, but you can easily produce a list/collection of the items in your dictionaries, like so:
var resultInformation = from outer in userInformation
from inner in outer.Value
let data = new { Outer = outer.Key, Inner = inner.Key, Value = inner.Value }
orderby data.Outer, data.Inner, data.Value
select data;
Or the query syntax equivalent:
var resultInformation = userInformation
.SelectMany(i => i.Value, (key, inner) => new { Outer = key, Inner = inner.Key, Value = inner.Value})
.OrderBy(e => e.Outer)
.ThenBy(e => e.Inner)
.ThenBy(e => e.Value);
Update: Based on your clarifying comment, I think what you really want is something more like this:
var resultInformation =
from student in userInformation
orderby student.Key
select new
{
studentId = student.Key,
courses =
from courseScore in student.Value
orderby courseScore.Value descending
select new {
course = courseScore.Key,
score = courseScore.Value
}
};

Related

Get count of same value on table

I have this class where the query must result in this list a property.
This property must check on table how many duplicated exists.
This code works, but its very slow. can you help me ?
var lst = _uow.Repository.GetAll();
var query =
from p in lst
select new GetRfqResponse
{
ID = p.ID,
//bad performance
Count = lst.Where(x => x.Property == p.Property).AsQueryable().Count(),
//
};
Counting in a queryable list can be easily achieved using the Count() function:
// Find duplicated names
var byName = from s in studentList
group s by s.StudentName into g
select new { Name = g.Key, Count = g.Count() };
Check this fiddle to see it running.
Below is for InMemory
GroupBy should come to help.
var propertyGroupedList = list.GroupBy(l=>l.Property);
var query = list.Select(l => new GetRfqResponse{
Id = l.Id,
Count = propertyGroupedList.First(g=> g.Key == l.Property).Count()
});
Or you can create a dictionary with key as "Property" and value as count, then you will have to loop just once to store the count.
This allows you to get count in constant time
Dictionary<string, int> map = new Dictionary<string, int>();
foreach (var item in lst)
{
if (!map.ContainsKey(lst.Property))
{
map.Add(item.Property, 1);
}
else
map[item.Property]++;
}
var z = lst.Select(l => new GetRfqResponse{
Id = l.ID,
Count = map[l.Property]
});

create n lists of list linq

I have a simple list
var list =
AppUtils.db.GetDataTable("dbo.RankSelectChart", view) // stopred procedure and getting datatable
.AsEnumerable()
.Select(i => new
{
Date = i.Field<DateTime>("lastDatetime"),
P1 = i.Field<decimal>("p1"),
P2 = i.Field<decimal>("P2"),
P3..... P(n)
}
)
.ToList()
.OrderBy(x => x.Date);
than i want to get a list of lists or dictionary like List<Dictionary<Datetime, Decimal>> means Dictionary<Date, P1> .... Dictionary<Date, P(n)>
how to write algorithm which is not depend how many P we have
As it stands, you will need to use reflection to access the properties:
var result = new[]{"P1", "P2", "P3", ...}.Select(p => list.ToDictionary(
i => i.Date,
i => i.GetType().GetProperty(p).GetValue(i)));
However, if you could avoid creating your list in the first place and just pull from the data table directly, it may be easier.
var dt = AppUtils.db.GetDataTable("dbo.RankSelectChart", view); // stopred procedure and getting datatable
var pColumns = dt.Columns.Cast<DataColumn>()
.Where(c => c.ColumnName.StartsWith("p"));
var result = pColumns
.Select(p => dt.AsEnumerable().ToDictionary(
i => i.Field<DateTime>("lastDatetime"),
i => i.Field<DateTime>(p.ColumnName)))
.ToList();
If 'Date' is unique for all records in 'list', then you can use reflection to get the P(i) value for a record in list. Like so:
// build sample data
var list = Enumerable.Range(0, 10)
.ToList()
.Select(x => new {
Date = DateTime.Now.AddMinutes(x),
P1 = new Decimal(x),
P2 = new Decimal(x + 1),
P3 = new Decimal(x + 2)
})
.ToList();
// list partionned by date; assumes that Date is unique in list
List<Dictionary<DateTime, Decimal>> partitionedList;
if (list.Count == 0) {
partitionedList = new List<Dictionary<DateTime, Decimal>>();
} else {
var n = 3;
var listElementType = list[0].GetType();
partitionedList = Enumerable.Range(1, n)
.Select(x => {
var prop = listElementType.GetProperty("P" + x);
var pList = list.ToDictionary(
ll => ll.Date,
ll => (Decimal)prop.GetValue(ll));
return pList;
})
.ToList();
}
If 'Date' is not unique, then it cannot be the key to a dictionary and the desired data structure is not achievable.

Linq How to Group By

I have a query like below. I want to group my values by "RapId"
Result must be come like this:
RaporId 1, List of UserId 15,24,23
RaporId 2, List of UserId 18,45,57
var sorgu = (from ra in Model1
join me in Model2
on ra.RapId equals me.RapId
select new
{
RapId = ra.RapId,
UserId= ra.RaportorId,
})
.GroupBy(x=>x.RapId )
.SelectMany(x => x)
.ToList();
var results = sorgu.GroupBy(p => p.RapId , p => p.UserId,
(key, g) => new { RapId = key, UserId= g.ToList() });
I get an error like this
> Error 39 Cannot convert lambda expression to type
> 'System.Collections.Generic.IEqualityComparer<AnonymousType#1>'
> because it is not a delegate type
What's wrong with this query?
Compiler thinks you are trying to use this overload: But you are passing a lambda expressions instead of IEqualityComparer.I think you just need to remove p => p.UserId :
var results = sorgu.GroupBy(p => p.RapId,
(key, g) => new { RapId = key, UserId= g.ToList() });

Converting SQL to Linq with groupby, sum and count

I would like to do a group by and on that a sum and a count. I don't seem to be able to create the solution in linq. How can I convert my query to linq?
SELECT HistoricalBillingProductGroup,
COUNT(*),
BillingPeriod,
SUM(TotalMonthlyChargesOtcAndMrc)
FROM [x].[dbo].[tblReport]
group by BillingPeriod, HistoricalBillingProductGroup
order by BillingPeriod
This is what I got sofar in Linq
var result =
context.Reports.GroupBy(x => new {x.BillingPeriod, x.HistoricalBillingProductGroup})
.Select(x => new StatisticsReportLine
{
HistoricalBillingGroup = x.FirstOrDefault().HistoricalBillingProductGroup,
BillingPeriod = x.FirstOrDefault().BillingPeriod,
CountOfRows = x.Count(),
SumOfAmount = x.Sum(p => p.TotalMonthlyChargesOtcAndMrc) ?? 0
})
.ToString();
The query I get from this is enormous and takes a very long time to load. In SQL its a matter of milliseconds. I hardly doubt this is the solution.
I believe the calls to x.FirstOrDefault() are the source of your problem. Each one of these will result in a very costly inner query inside the SELECT clause of the generated SQL.
Try using the Key property of the IGrouping<T> instead :
var result = context.Reports
.GroupBy(x => new {x.BillingPeriod, x.HistoricalBillingProductGroup})
.OrderBy(x => x.Key.BillingPeriod)
.Select(x => new StatisticsReportLine
{
HistoricalBillingProductGroup = x.Key.HistoricalBillingProductGroup,
BillingPeriod = x.Key.BillingPeriod,
CountOfRows = x.Count(),
SumOfAmount = x.Sum(p => p.TotalMonthlyChargesOtcAndMrc) ?? 0
});
Or if you prefer query syntax:
var result =
(from r in context.Reports
group r by new { r.BillingPeriod, r.HistoricalBillingProductGroup } into g
orderby g.Key.BillingPeriod
select new StatisticsReportLine
{
HistoricalBillingProductGroup = g.Key.HistoricalBillingProductGroup,
BillingPeriod = g.Key.BillingPeriod,
CountOfRows = g.Count(),
SumOfAmount = x.Sum(p => p.TotalMonthlyChargesOtcAndMrc) ?? 0
});
You could try this one:
var result = context.Reports
.GroupBy(x => new {x.BillingPeriod, x.HistoricalBillingProductGroup})
.Select(x => new StatisticsReportLine
{
HistoricalBillingGroup = x.Key.HistoricalBillingProductGroup,
BillingPeriod = x.Key.BillingPeriod,
CountOfRows = x.Count(),
SumOfAmount = x.Sum(p => p.TotalMonthlyChargesOtcAndMrc) ?? 0
}).ToString();
In the above query you make a group by on two properties, BillingPeriod and HistoricalBillingProductGroup. So in each group that will be created, you will have a key, that will be consisted by these two properties.

How do I find a subset of items in two sets of data that partially differ?

I am trying to get the subset of items in dataA that are in dataB, and have different values of property c. The properties a and b can be used as an index, so I have tried to filter out only the useful pairs then check to see if they have a different c value.
This is the linq expression I came up with, and it does work, but It seems like there has to be a better/faster way of finding this subset.
var itemsInBoth = from item in dataA
from item2 in dataB
where item.a == item2.a && item.b == item2.b
select new
{
first= item,
second = item2
};
var haveDifferentC = from item in itemsInBoth
where item.first.c != item.second.c
select item.first;
Based on the answer provided by David B, I eventually settled on a slightly modified version of his method. Although the differences are minor, I thought I would share this, primarily to show a version that for those (like me) that prefer the expressive syntax.
Also, instead of grouping, I decided to use an anonymous key/value pair to simplify the structure.
var dictA = (from item in dataA
select new
{
key = CreateIndexValue(item.a, item.b),
value = item
}).ToDictionary(kv => kv.key, kv => kv.value);
var dictB = (from item in dataB
select new
{
key = CreateIndexValue(item.a, item.b),
value = item
}).ToDictionary(kv => kv.key, kv => kv.value);
var filesInBoth = from item in dictA
where dictB.ContainsKey(item.Key)
select new
{
itemA = dictA[item.Key],
itemB = dictB[item.Key]
};
var differentSize = from item in filesInBoth
where item.itemA.c!= item.itemB.c
select item.itemA;
Faster? What you have there is O(n^2). Each item in the first list will fully iterate the items in the second list. You need to remove the redundant iteration in that join. One way to do that is to use another structure to do O(1) lookups for matchs.
Here's some untested (unchecked) code:
var dictionaryA = dataA
.GroupBy(item => new {a = item.a, b = item.b})
.ToDictionary(g => g.Key, g => g.ToList());
var dictionaryB = dataB
.GroupBy(item => new {a = item.a, b = item.b})
.ToDictionary(g => g.Key, g => g.ToList());
var results = dictionaryA
.Where(g1 => dictionaryB.ContainsKey(g1.Key))
.Select(g1 => new {g1 = g1, g2 = dictionaryB[g1.Key]})
.SelectMany(pair =>
pair.g1.SelectMany(item1 =>
pair.g2
.Where(item2 => item2.c != item1.c)
.Select(item2 => new {item1, item2})
)
);
Here's a simplified version if a,b pairs are unique in each list.
var dictionaryA = dataA
.ToDictionary(item => new {a = item.a, b = item.b}, item => item);
var dictionaryB = dataB
.ToDictionary(item => new {a = item.a, b = item.b}, item => item);
var results = dictionaryA
.Where(e1 => dictionaryB.ContainsKey(e1.Key))
.Select(e1 => new {i1 = e1.Value, i2 = dictionaryB[e1.Key]})
.Where(pair => pair.i1.c != pair.i2.c);

Categories

Resources