I have a grouped object that looks like
Dictionary<(ushort id, ushort sc), Timepoint[]> timepoints
and it would look like (1, 2) => [some string timepoints]
But I want to convert it to
Dictionary<ushort id, Timepoint[]>
and I want to aggregate that sc and have only the id. I tried:
test = timepoints.GroupBy(group => group.Key.id).ToDictionary(key => key.Key, value => value);
But I had no luck all.
Dictionary<ushort, IGrouping<ushort, KeyValuePair<(ushort id, ushort sc), Timepoint[]>>>
I think I am missing something.
You need to use a SelectMany to flatten out the arrays you are grouping together and turn them into one array first.
var test = timepoints
.GroupBy(kvp => kvp.Key.id)
.Select(grp => new { grp.Key, Values = grp.SelectMany(x => x.Value).ToArray() })
.ToDictionary(x => x.Key, x => x.Values);
Extended and tested version of #juharr's answer;
timepoints
.GroupBy(kvp => kvp.Key.id)
.Select(grp => new { grp.Key, Values = grp.SelectMany(x => x.Value).ToArray() })
.ToDictionary(x => x.Key, x => x.Values);
Here is the whole test console app you can try out:
static void Main(string[] args)
{
Dictionary<(ushort id, ushort sc), Timepoint[]> timepoints = new Dictionary<(ushort id, ushort sc), Timepoint[]>();
timepoints.Add((1, 1), new Timepoint[] { new Timepoint(1, "1,1,1"), new Timepoint(1, "1,1,2"), new Timepoint(1, "1,1,3") });
timepoints.Add((1, 2), new Timepoint[] { new Timepoint(1, "1,2,1"), new Timepoint(1, "1,2,2"), new Timepoint(1, "1,2,3") });
timepoints.Add((2, 1), new Timepoint[] { new Timepoint(1, "2,1,1"), new Timepoint(1, "2,1,2"), new Timepoint(1, "2,1,3") });
timepoints.Add((2, 2), new Timepoint[] { new Timepoint(1, "2,2,1"), new Timepoint(1, "2,2,2"), new Timepoint(1, "2,2,3") });
var test = timepoints
.GroupBy(kvp => kvp.Key.id)
.Select(grp => new { grp.Key, Values = grp.SelectMany(x => x.Value).ToArray() })
.ToDictionary(x => x.Key, x => x.Values);
}
class Timepoint
{
public Timepoint(int id, string name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; set; }
public string Name { get; set; }
}
Related
var list = new[]
{
new { maker="Volvo", type=1, model=15},
new { maker="Volvo", type=8, model=10},
new { maker="Volvo", type=8, model=100},
new { maker="Volvo", type=8, model=40},
new { maker="Volvo", type=6, model=5},
new { maker="Volvo", type=2, model=0},
new { maker="Volvo", type=1, model=2},
new { maker="GM", type=1, model=0},
new { maker="GM", type=0, model=20},
new { maker="GM", type=9, model=5},
new { maker="GM", type=9, model=50},
new { maker="GM", type=9, model=25},
};
var results = list
.GroupBy(x => x.maker, (key, g) => g.OrderByDescending(e => e.type).First())
.ToList();
Returns
{ maker = Volvo, type = 8, model = 10 }
{ maker = GM, type = 9, model = 5 }
Which is closed to what I want and if I keep extending it to
var results = list
.GroupBy(x => x.maker, (key, g) => g.OrderByDescending(e => e.type).GroupBy(z => z.type, (key1, y) => y.OrderByDescending(u => u.model).First()))
.ToList();
Returns nothing
vs. Expected result
{ maker = Volvo, type = 8, model = 100 }
{ maker = GM, type = 9, model = 50 }
So how do I fix this?
You just need a ThenByDescending instead of another GroupBy:
var results = list
.GroupBy(x => x.maker,
(key, g) => g.OrderByDescending(e => e.type)
.ThenByDescending(e => e.model)
.First())
.ToList();
I have a list of guids as string:
This is how i retrive my list of string guids:
List<string> p0 = ctx.PrProjectRating.Select(k => k).GroupBy(g => new { g.PrPIdG }, (key, group) => new { sumR = group.Sum(k => k.PrValue), pidG = key.PrPIdG }).Select(t => t.pidG).ToList();
Now i have another list that contains a field called pidG but this list needs to be ordered by the list of guid strings above.
How do i achiveve this.
i tried:
List<PProject> p = p.OrderBy(c => p0.Contains(c.PIdG)).ToList();
but still the list is not ordered by the string guids in the first list "p0"
You have to do join here
List<string> p0 = ctx.PrProjectRating
.Select(k => k)
.GroupBy(g => new { g.PrPIdG }, (key, group) =>
new { sumR = group.Sum(k => k.PrValue), pidG = key.PrPIdG })
.Select(t => t.pidG).ToList();
var result = p0.Join(p, x => x, c => c.PIdG, (x, c) => c)
.ToList()
I have a Dictionary<Guid,IList<string>> which shows all the names an entity can have.
I want to convert this to see all the names mapped to all the entities.
so:
[["FFF" => "a", "b"],
["EEE" => "a", "c"]]
Becomes
[["a" => "FFF", "EEE"],
["b" => "FFF"],
["c" => "EEE"]]
I know this is easy to do with foreaches but I'm wondering if there is a way with LINQ / ToDictionary?
private static void Main(string[] args)
{
var source = new Dictionary<Guid, IList<string>>
{
{ Guid.NewGuid(), new List<string> { "a", "b" } },
{ Guid.NewGuid(), new List<string> { "b", "c" } },
};
var result = source
.SelectMany(x => x.Value, (x, y) => new { Key = y, Value = x.Key })
.GroupBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.Select(y => y.Value).ToList());
foreach (var item in result)
{
Console.WriteLine($"Key: {item.Key}, Values: {string.Join(", ", item.Value)}");
}
}
var dic = new Dictionary<string, List<string>>()
{
{"FFF", new List<string>(){"a", "b"}},
{"EEE", new List<string>(){"a", "c"}}
};
var res = dic.SelectMany(x => x.Value, (x,y) => new{Key = y, Value = x.Key})
.ToLookup(x => x.Key, x => x.Value);
Dictionary<int,IList<string>> d = new Dictionary<int ,IList<string>>(){
{1,new string[]{"a","b"}},
{2,new string[]{"a","d"}},
{3,new string[]{"b","c"}},
{4,new string[]{"x","y"}}};
d.SelectMany(kvp => kvp.Value.Select(element => new { kvp.Key, element}))
.GroupBy(g => g.element, g => g.Key)
.ToDictionary(g => g.Key, g => g.ToList());
This query below doesn't work because String.Join is not translatable.
PostgreSQL has the string_agg(expression, delimiter) feature though.
Is there anyway to use it from Linq?
var vwTourWithCategorieses = Context.Tours
.Join(Context.TourCategories, t => t.TourId, tc => tc.TourId,
(t, tc) => new { t.TourId, t.Name, tc.CategoryId})
.Join(Context.Categories, x => x.CategoryId, c => c.CategoryId,
(x, c) => new { x.TourId, TourName = x.Name, CategoryName = c.Name})
.GroupBy(x => new { x.TourId, x.TourName },
(key, c) => new VwTourWithCategories
{
TourId = key.TourId,
Name = key.TourName,
Categories = string.Join(",", c.Select(i => i.CategoryName))
})
.ToList();
Yes, unfortunately String.Join is not supported by EF, but I think you could project the result that you expect using Linq to objects after you materialize your query:
var query= Context.Tours
.Join(Context.TourCategories, t => t.TourId, tc => tc.TourId,
(t, tc) => new { t.TourId, t.Name, tc.CategoryId})
.Join(Context.Categories, x => x.CategoryId, c => c.CategoryId,
(x, c) => new { x.TourId, TourName = x.Name, CategoryName = c.Name})
.GroupBy(x => new { x.TourId, x.TourName }).ToList()
var result=query.Select( g=> new VwTourWithCategories
{
TourId = g.Key.TourId,
Name = g.Key.TourName,
Categories = string.Join(",", g.Select(i => i.CategoryName))
});
If you want to see all the CLR methods that are supported, you can check this link.
Update
Your query could be simpler if you use navigation properties. I think that is a many to many relationship, so you could do something like this:
var query= Context.Tours.Select(t=> new
{
t.TourId,
t.Name,
CategoryNames = t.TourCategories.Select(tc=>tc.Category.Name)
}
).ToList();
var result=query.Select( g=> new VwTourWithCategories
{
TourId = g.Key.TourId,
Name = g.Key.TourName,
Categories = string.Join(",", g.Select(i => i.CategoryName))
});
I have a list of key/value pairs in the following form:
[{John:6},{Alex:100},{Peter:4},{Peter,John:5},{Alex,Kati:1}]
I wonder if there is a simple linq expression I can use to translate the list into
[{John:11},{Alex:101},{Peter:9},{Kati:1}]
ie split string by comma and adjust counts.
the list above is coming from following LINQ
var list = people.Where(a => !string.IsNullOrWhiteSpace(a.Name))
.GroupBy(a => a.Name.Trim()).Select(a => new User { Name = a.Key, Items= a.Count() });
With
var list = new[]
{
new KeyValuePair<string, int>("John", 6),
new KeyValuePair<string, int>("Alex", 100),
new KeyValuePair<string, int>("Peter", 4),
new KeyValuePair<string, int>("Peter,John", 5),
new KeyValuePair<string, int>("Alex,Kati", 1)
};
this grouping
var modifiedList = list.SelectMany(p => p.Key.Split(',').Select(n => new {Name = n, Number = p.Value}))
.GroupBy(p => p.Name).Select(g => new KeyValuePair<string, int>(g.Key, g.Sum(r => r.Number)));
gives you the output
{[John, 11]}
{[Alex, 101]}
{[Peter, 9]}
{[Kati, 1]}
Try this:
var list = new List<KeyValuePair<string, int>>
{
new KeyValuePair<string,int>("John", 6),
new KeyValuePair<string,int>("Alex", 100),
new KeyValuePair<string,int>("Peter", 4),
new KeyValuePair<string,int>("Peter,John", 5),
new KeyValuePair<string,int>("Alex,Kati", 1)
};
var result = list.SelectMany(x => x.Key.Split(','),
(x, y) => new KeyValuePair<string, int>(y, x.Value))
.GroupBy(x => x.Key)
.ToDictionary(key => key.Key, value => value.Sum(x => x.Value));
Dictionary<string, int> result = keyVals
.SelectMany(kv => kv.Key.Split(',').Select(name => new{ name, kv.Value }))
.GroupBy(x => x.name)
.ToDictionary(xg => xg.Key, xg => xg.Sum(x => x.Value));
Result:
{[John, 11]}
{[Alex, 101]}
{[Peter, 9]}
{[Kati, 1]}
Demo
So you have classes like this
public class Person
{
public string Name { get; set; }
}
public class User
{
public string Name { get; set; }
public int Items { get; set; }
}
If I understand correctly you want to count how many times each name occurs
var people = new[]
{
new Person { Name = "John" },
new Person { Name = "John,Alex" },
new Person { Name = "Alex" },
new Person { Name ="Peter,John" }
};
var list = people.SelectMany(p => p.Name.Split(','))
.GroupBy(n => n)
.Select(g => new User { Name = g.Key, Items = g.Count() });