I have a variable of type Dictionary<MyType, List<MyOtherType>>
I want to convert it to a Lookup<MyType, MyOtherType>.
I wanted to use Lambda functions to first, flatten the dictionary and then convert this to Lookup using the ToLookup(). I got stuck with the dictionary. I thought about using SelectMany but can't get it working. Anyone has got an idea how to do it?
Same as Jon's method, but avoiding the creation of an anonymous type:
var lookup = dictionary
.SelectMany(p => p.Value, Tuple.Create)
.ToLookup(p => p.Item1.Key, p => p.Item2);
How about:
var lookup = dictionary.SelectMany(pair => pair.Value,
(pair, Value) => new { pair.Key, Value })
.ToLookup(pair => pair.Key, pair => pair.Value);
It does feel like a little bit of a waste doing this when the dictionary already has all the information grouped appropriately, but I can't see a simple way round that. Of course you could implement ILookup<TKey, TValue> yourself with a wrapper around the dictionary...
Already a few answers here, but putting this here for reference.
This flips a dictionary with a list of values, to having those values as the keys of look up list.
var myLookup = myDict.SelectMany(p => p.Value,
(pair, id) => Tuple.Create(id, pair.Key))
.ToLookup(p => p.Item1, p => p.Item2);
Annotated
var myLookup = myDict.SelectMany(
// specify that the select many is to be based off the Value which is a list of items
p => p.Value,
// Using the individual items from that list, create a tuple of that item and the dictionary key it was under
(pair, id) => Tuple.Create(id, pair.Key))
// use the item as the lookup key, and put the original dictionary key (that
// had that value underneath them) in the list of lookup values.
.ToLookup(p => p.Item1, p => p.Item2);
Not an answer for the question, but I think this is related information and should be posted here.
There is some edge cases you should take into account. All of them about items of dictionary, which have key, but don't have value.
This is expected behavior. Dictionary and Lookup designed for different purposes.
var dic = new Dictionary<bool, IEnumerable<bool?>> { [true] = null };
var lookup = dic.ToLookup();
Assert.AreEqual(1, dic.Count);
Assert.AreEqual(0, lookup.Count);
Assert.IsTrue(dic.ContainsKey(true));
Assert.IsFalse(lookup.Contains(true));
Assert.IsFalse(dic.ContainsKey(false));
Assert.IsFalse(lookup.Contains(false));
dic[false] -> Exception
lookup[false] -> bool?[0]
Late to the party but I think this should work, without needing to enumerate everything again and create temporary tuples/anonymous types.
public static ILookup<TKey, TElement> ToLookup<TKey, TElement>(
this IEnumerable<TKey> keys,
Func<TKey, IEnumerable<TElement>> selector)
{
return new ManualLookup<TKey, TElement>(keys, selector);
}
private class ManualLookup<TKey, TElement> : ILookup<TKey, TElement>
{
private IEnumerable<TKey> _keys;
private Func<TKey, IEnumerable<TElement>> _selector;
public ManualLookup(IEnumerable<TKey> keys, Func<TKey, IEnumerable<TElement>> selector)
{
_keys = keys;
_selector = selector;
}
public IEnumerable<TElement> this[TKey key] => _selector(key);
public int Count => _keys.Count();
public bool Contains(TKey key) => _keys.Contains(key);
public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator() => _keys
.Select(key => new ManualGrouping<TKey, TElement>(key, _selector(key)))
.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
private class ManualGrouping<TKey, TElement> : IGrouping<TKey, TElement>
{
private TKey _key;
private IEnumerable<TElement> _enumerable;
public ManualGrouping(TKey key, IEnumerable<TElement> enumerable)
{
_key = key;
_enumerable = enumerable;
}
public TKey Key => _key;
public IEnumerator<TElement> GetEnumerator() => _enumerable.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Then you can do something like:
Dictionary<MyType, List<MyOtherType>> dictionary;
return dictionary.Keys.ToLookup(key =>
{
if (dictionary.TryGetValue(key, out var list)
{
return list;
}
return Enumerable.Empty<MyOtherType>();
});
Related
Say I have an entity that I want to query with ranking applied:
public class Person: Entity
{
public int Id { get; protected set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
In my query I have the following:
Expression<Func<Person, object>> orderBy = x => x.Name;
var dbContext = new MyDbContext();
var keyword = "term";
var startsWithResults = dbContext.People
.Where(x => x.Name.StartsWith(keyword))
.Select(x => new {
Rank = 1,
Entity = x,
});
var containsResults = dbContext.People
.Where(x => !startsWithResults.Select(y => y.Entity.Id).Contains(x.Id))
.Where(x => x.Name.Contains(keyword))
.Select(x => new {
Rank = 2,
Entity = x,
});
var rankedResults = startsWithResults.Concat(containsResults)
.OrderBy(x => x.Rank);
// TODO: apply thenby ordering here based on the orderBy expression above
dbContext.Dispose();
I have tried ordering the results before selecting the anonymous object with the Rank property, but the ordering ends up getting lost. It seems that linq to entities discards the ordering of the separate sets and converts back to natural ordering during both Concat and Union.
What I think I may be able to do is dynamically transform the expression defined in the orderBy variable from x => x.Name to x => x.Entity.Name, but I'm not sure how:
if (orderBy != null)
{
var transformedExpression = ???
rankedResults = rankedResults.ThenBy(transformedExpression);
}
How might I be able to use Expression.Lambda to wrap x => x.Name into x => x.Entity.Name? When I hard code x => x.Entity.Name into the ThenBy I get the ordering that I want, but the orderBy is provided by the calling class of the query, so I don't want to hard-code it in. I have it hardcoded in the example above for simplicity of explanation only.
This should help. However you are going to have to concrete up the Anonymous type for this to work. My LinqPropertyChain will not work with it, since its going to be difficult to create the Expression<Func<Anonymous, Person>> whilst its still Anonymous.
Expression<Func<Person, object>> orderBy = x => x.Name;
using(var dbContext = new MyDbContext())
{
var keyword = "term";
var startsWithResults = dbContext.People
.Where(x => x.Name.StartsWith(keyword))
.Select(x => new {
Rank = 1,
Entity = x,
});
var containsResults = dbContext.People
.Where(x => !startsWithResults.Select(y => y.Entity.Id).Contains(x.Id))
.Where(x => x.Name.Contains(keyword))
.Select(x => new {
Rank = 2,
Entity = x,
});
var rankedResults = startsWithResults.Concat(containsResults)
.OrderBy(x => x.Rank)
.ThenBy(LinqPropertyChain.Chain(x => x.Entity, orderBy));
// TODO: apply thenby ordering here based on the orderBy expression above
}
public static class LinqPropertyChain
{
public static Expression<Func<TInput, TOutput>> Chain<TInput, TOutput, TIntermediate>(
Expression<Func<TInput, TIntermediate>> outter,
Expression<Func<TIntermediate, TOutput>> inner
)
{
Console.WriteLine(inner);
Console.WriteLine(outter);
var visitor = new Visitor(new Dictionary<ParameterExpression, Expression>
{
{inner.Parameters[0], outter.Body}
});
var newBody = visitor.Visit(inner.Body);
Console.WriteLine(newBody);
return Expression.Lambda<Func<TInput, TOutput>>(newBody, outter.Parameters);
}
private class Visitor : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> _replacement;
public Visitor(Dictionary<ParameterExpression, Expression> replacement)
{
_replacement = replacement;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (_replacement.ContainsKey(node))
return _replacement[node];
else
{
return node;
}
}
}
}
Figured out a way to do this with less Explicite Generics.
Expression<Func<Person, object>> orderBy = x => x.Name;
Expression<Func<Foo, Person>> personExpression = x => x.Person;
var helper = new ExpressionChain(personExpression);
var chained = helper.Chain(orderBy).Expression;
// Define other methods and classes here
public class ExpressionChain<TInput, TOutput>
{
private readonly Expression<Func<TInput, TOutput>> _expression;
public ExpressionChain(Expression<Func<TInput, TOutput>> expression)
{
_expression = expression;
}
public Expression<Func<TInput, TOutput>> Expression { get { return _expression; } }
public ExpressionChain<TInput, TChained> Chain<TChained>
(Expression<Func<TOutput, TChained>> chainedExpression)
{
var visitor = new Visitor(new Dictionary<ParameterExpression, Expression>
{
{_expression.Parameters[0], chainedExpression.Body}
});
var lambda = Expression.Lambda<Func<TInput, TOutput>>(newBody, outter.Parameters);
return new ExpressionChain(lambda);
}
private class Visitor : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> _replacement;
public Visitor(Dictionary<ParameterExpression, Expression> replacement)
{
_replacement = replacement;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (_replacement.ContainsKey(node))
return _replacement[node];
else
{
return node;
}
}
}
}
Since you're ordering by Rank first, and the Rank values are identical within each sequence, you should be able to just sort independently and then concatenate. It sounds like the hiccup here would be that, according to your post, Entity Framework isn't maintaining sorting across Concat or Union operations. You should be able to get around this by forcing the concatenation to happen client-side:
var rankedResults = startsWithResults.OrderBy(orderBy)
.AsEnumerable()
.Concat(containsResults.OrderBy(orderBy));
This should render the Rank property unnecessary and probably simplify the SQL queries being executed against your database, and it doesn't require mucking about with expression trees.
The downside is that, once you call AsEnumerable(), you no longer have the option of appending additional database-side operations (i.e., if you chain additional LINQ operators after Concat, they will use the LINQ-to-collections implementations). Looking at your code, I don't think this would be a problem for you, but it's worth mentioning.
Today I'm mapping my objects to DTO like this.
public IEnumerable<ArticleDTO> GetArticlesBasedOnCategorySection(int catSection, int headCategoryID, string customerREFID)
{
return _articleRepository.GetArticlesByCategory(catSection, headCategoryID, customerREFID).Select(a => Mapper.ToDTO(a)).ToList();
}
But inside the variable I have another list that I want to map in a similar way.
Is it possible to write this all in one line like this or do I have to write an foreach loop then map a.List.
How about returning the Article and its items in an anonymous object?
public IEnumerable<ArticleDTO> GetArticlesBasedOnCategorySection(int catSection, int headCategoryID, string customerREFID)
{
return _articleRepository
.GetArticlesByCategory(catSection, headCategoryID, customerREFID)
.Select(a => new
{
Article = Mapper.ToDTO(a),
Items = a.Items.Select(b => Mapper.ToDTO(b)).ToList()
})
.ToList();
}
One way is to use a lambda that has multiple statements. I'm not sure if this can be considered a one liner, and multi-statement lambdas aren't very LINQ-y.
public IEnumerable<ArticleDTO> GetArticlesBasedOnCategorySection(int catSection, int headCategoryID, string customerREFID)
{
return _articleRepository
.GetArticlesByCategory(catSection, headCategoryID, customerREFID)
.Select(a =>
{
ArticleDTO article = Mapper.ToDTO(a);
article.Items = a.Items.Select(b => Mapper.ToDTO(b)).ToList();
return article;
})
.ToList();
}
If ArticleDTO has a copy constructor you could write it like this:
public IEnumerable<ArticleDTO> GetArticlesBasedOnCategorySection(int catSection, int headCategoryID, string customerREFID)
{
return _articleRepository
.GetArticlesByCategory(catSection, headCategoryID, customerREFID)
.Select(a => new ArticleDTO(Mapper.ToDTO(a))
{
Items = a.Items.Select(b => Mapper.ToDTO(b)).ToList()
})
.ToList();
}
You could also map the items in a constructor or in Mapper.ToDTO(a).
Given an Expression<Func<T, TValue>> (like m => m.Name) and an index, I'd like to be able to transform my expression to m => m[index].Name). And I must admit I'm stuck...
I give you the actual scenario if you want the "Why the hell" (and maybe find a better way).
Scenario : imagine a Server Side Editable Grid (without javascript).
I build my grid with an helper which look like that :
#(Html.Grid(Model)
.Columns(columns => {
columns.Edit(m => m.Name);
columns.Edit(m => m.Code);
})
.AsEditable());
Model is an IQueryable<T>
m => m.Name is an Expression<Func<T, TValue>> (TValue is string)
m => m.Code is an Expression<Func<T, TValue>> (TValue is int)
When rendering my view, I'd like to display an html form.
The IQueryable<T> is enumerated (order, pagination). => ok
So I'll have a List<T> of 5, 10 or 20 T items.
And Name and Code should be represented as TextBox, using a classic HtmlHelper.TextBoxFor(Expression<Func<T, TValue>>) (no problem to create the HtmlHelper<T>)
But as I've got a list, if I want correct Model binding, I can't use directly m => m.Name, but should use m => m[indexOfItem in List<T>].Name
Edit : more details :
So let's say we have an entity class
public class Test {
public int Id {get;set;}
public string Name {get;set;}
public string Code {get;set;}
}
Then, a method retrieving an IQueryable<Test>
Then a view
#model IQueryable<Test>
#(Html.Grid(Model)
.Columns(columns => {
columns.Edit(m => m.Name);
columns.Edit(m => m.Code);
})
.AsEditable());
as you see, Model given as parameter is IQueryable<Test>.
m => m.Name
and
m => m.Code
are just properties of the Model (which I wanna display as TextBox in my grid).
The model is an IQueryable<T> (not an IEnumerable<T>), because the Grid manages ordering and Pagination, so that my controller and service layer don't need to know about pagination and ordering.
Is it clearer ?
This could be easily achieved by writing a custom ExpressionVisitor:
public static class ExpressionExtensions
{
private class Visitor : ExpressionVisitor
{
private readonly int _index;
public Visitor(int index)
{
_index = index;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return Expression.ArrayIndex(GetArrayParameter(node), Expression.Constant(_index));
}
public ParameterExpression GetArrayParameter(ParameterExpression parameter)
{
var arrayType = parameter.Type.MakeArrayType();
return Expression.Parameter(arrayType, parameter.Name);
}
}
public static Expression<Func<T[], TValue>> BuildArrayFromExpression<T, TValue>(
this Expression<Func<T, TValue>> expression,
int index
)
{
var visitor = new Visitor(index);
var nexExpression = visitor.Visit(expression.Body);
var parameter = visitor.GetArrayParameter(expression.Parameters.Single());
return Expression.Lambda<Func<T[], TValue>>(nexExpression, parameter);
}
}
and then you could use this extension method like this:
Expression<Func<Test, string>> ex = m => m.Code;
Expression<Func<Test[], string>> newEx = ex.BuildArrayFromExpression(1);
Well, got something (ugly, just for testing purpose) working, but it makes things unclear, so I'll wait for a better solution or build my input another way :
public static Expression<Func<T[], TValue>> BuildArrayFromExpression<T, TValue>(this Expression<Func<T, TValue>> expression, int index)
{
var parameter = Expression.Parameter(typeof(T[]), "m");
Expression body = Expression.ArrayIndex(parameter, Expression.Constant(index));
var type = typeof(T);
var properties = expression.Body.ToString().Split('.');//ugly shortcut for test only
foreach (var property in properties.Skip(1))
{
var pi = type.GetProperty(property);
body = Expression.Property(body, type.GetProperty(property));
type = pi.PropertyType;
}
return Expression.Lambda<Func<T[], TValue>>(body, parameter);
}
used in a RenderMethod, called for each line of my List<T> (the paginated /ordered list returned from my IQueryable<T>)
public override ... RenderContent(T dataItem) {
var helper = new HtmlHelper<T[]>(GridModel.Context, new GridViewDataContainer<T[]>(GridModel.PaginatedItems.ToArray(), GridModel.Context.ViewData));
var modifiedExpression = Expression.BuildArrayFromExpression(GridModel.PaginatedItems.IndexOf(dataItem));//GridModel.PaginatedItems is the List<T> returned when "executing" the IQueryable<T>
var textBox = helper.TextBoxFor(modifiedExpression);
...
}
I have an object of type IEnumerable<IEnumerable<int>>. I need to sort the ints within the inner IEnumerable<int> but I am not sure how to do this.
I attempted to convert IEnumerable<IEnumerable<int>> to List<List<int>> to make the sort easier with this code:
var sortedResults = results.ForEach(x => x.ToList());
But I receive the error "Cannot assign void to implicitly-typed variable"
I also receive the same error if I to the sorting and the conversion all at once:
var sortedResults = results.ToList().ForEach(x => x.ToList().Sort((a ,b) => a.CompareTo(b))));
What is the best way to do this?
Just use OrderBy - since these are integers that means you can use the number itself as criteria:
sortedResults = results.Select(x => x.OrderBy(num => num));
Above assumes you want the output to be just a IEnumerable<IEnumerable<int>> if you need lists, use ToList() were needed.
var sortedResults = results.Select(list => list.OrderBy(x => x));
I tried to draw an example of how to order using IEnumerable
I think I can help you understand how it works.
Observe the example, I hope have helped to you understand
Example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
class Person
{
public string name { get; set; }
public int personage { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
Person[] p ={new Person{name="Alice",personage=10 },
new Person{name="Alex",personage=11},
new Person{name="Danny",personage=12}};
IEnumerable<Person> query = p.OrderBy(ps => ps.personage);
foreach (Person item in query)
{
textBox1.Text += item.name + item.personage.ToString() + Environment.NewLine;
}
}
Result of this is:
Alice10
Alex11
Danny12
I've got an ILookup generated by some complicated expression. Let's say it's a lookup of people by last name. (In our simplistic world model, last names are unique by family)
ILookup<string, Person> families;
Now I've got two queries I'm interested in how to build.
First, how would I filter by last name?
var germanFamilies = families.Where(family => IsNameGerman(family.Key));
But here, germanFamilies is an IEnumerable<IGrouping<string, Person>>; if I call ToLookup() on it, I'd best bet would get an IGrouping<string, IGrouping<string, Person>>. If I try to be smart and call SelectMany first I'd end up with the computer doing a lot of unnecessary work. How would you convert this enumeration into a lookup easily?
Second, I'd like to get a lookups of adults only.
var adults = families.Select(family =>
new Grouping(family.Key, family.Select(person =>
person.IsAdult())));
Here I'm faced with two problems: the Grouping type doesn't exist (except as an internal inner class of Lookup), and even if it did we'd have the problem discussed above.
So, apart from implementing the ILookup and IGrouping interfaces completely, or make the computer do silly amounts of work (regrouping what has already been grouped), is there a way to alter existing ILookups to generate new ones that I missed?
(I'm going to assume you actually wanted to filter by last name, given your query.)
You can't modify any implementation of ILookup<T> that I'm aware of. It's certainly possible to implement ToLookup with an immutable lookup, as you're clearly aware :)
What you could do, however, is to change to use a Dictionary<string, List<Person>>:
var germanFamilies = families.Where(family => IsNameGerman(family.Key))
.ToDictionary(family => family.Key,
family.ToList());
That approach also works for your second query:
var adults = families.ToDictionary(family => family.Key,
family.Where(person => persion.IsAdult)
.ToList());
While that's still doing a bit more work than we might think necessary, it's not too bad.
EDIT: The discussion with Ani in the comments is worth reading. Basically, we're already going to be iterating over every person anyway - so if we assume O(1) dictionary lookup and insertion, we're actually no better in terms of time-complexity using the existing lookup than flattening:
var adults = families.SelectMany(x => x)
.Where(person => person.IsAdult)
.ToLookup(x => x.LastName);
In the first case, we could potentially use the existing grouping, like this:
// We'll have an IDictionary<string, IGrouping<string, Person>>
var germanFamilies = families.Where(family => IsNameGerman(family.Key))
.ToDictionary(family => family.Key);
That is then potentially much more efficient (if we have many people in each family) but means we're using groupings "out of context". I believe that's actually okay, but it leaves a slightly odd taste in my mouth, for some reason. As ToLookup materializes the query, it's hard to see how it could actually go wrong though...
For your first query, what about implementing your own FilteredLookup able to take advantage of coming from another ILookup ?
(thank to Jon Skeet for the hint)
public static ILookup<TKey, TElement> ToFilteredLookup<TKey, TElement>(this ILookup<TKey, TElement> lookup, Func<IGrouping<TKey, TElement>, bool> filter)
{
return new FilteredLookup<TKey, TElement>(lookup, filter);
}
With FilteredLookup class being:
internal sealed class FilteredLookup<TKey, TElement> : ILookup<TKey, TElement>
{
int count = -1;
Func<IGrouping<TKey, TElement>, bool> filter;
ILookup<TKey, TElement> lookup;
public FilteredLookup(ILookup<TKey, TElement> lookup, Func<IGrouping<TKey, TElement>, bool> filter)
{
this.filter = filter;
this.lookup = lookup;
}
public bool Contains(TKey key)
{
if (this.lookup.Contains(key))
return this.filter(this.GetGrouping(key));
return false;
}
public int Count
{
get
{
if (count >= 0)
return count;
count = this.lookup.Where(filter).Count();
return count;
}
}
public IEnumerable<TElement> this[TKey key]
{
get
{
var grp = this.GetGrouping(key);
if (!filter(grp))
throw new KeyNotFoundException();
return grp;
}
}
public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator()
{
return this.lookup.Where(filter).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private IGrouping<TKey, TElement> GetGrouping(TKey key)
{
return new Grouping<TKey, TElement>(key, this.lookup[key]);
}
}
and Grouping:
internal sealed class Grouping<TKey, TElement> : IGrouping<TKey, TElement>
{
private readonly TKey key;
private readonly IEnumerable<TElement> elements;
internal Grouping(TKey key, IEnumerable<TElement> elements)
{
this.key = key;
this.elements = elements;
}
public TKey Key { get { return key; } }
public IEnumerator<TElement> GetEnumerator()
{
return elements.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
So basically your first query will be:
var germanFamilies = families.ToFilteredLookup(family => IsNameGerman(family.Key));
This allows you to avoid re-flattening-filtering-ToLookup, or creating a new dictionary (and so hashing keys again).
For the second query the idea will be similar, you should just create a similar class not filtering for the whole IGrouping but for the elements of the IGrouping.
Just an idea, maybe it could not be faster than other methods :)
The Lookup creates an index with a Key type and a value type generic indexer. You can added to a lookup and remove from a lookup by using concat for add and iterate and removing the key items in a temp list then rebuilding the lookup. The look up then works like a dictionary by retrieving the value type by a key.
public async Task TestILookup()
{
// Lookup<TKey,TElement>
List<Product> products = new List<Product>
{
new Product { ProductID = 1, Name = "Kayak", Category = "Watersports", Price = 275m },
new Product { ProductID = 2, Name = "Lifejacket", Category = "Watersports", Price = 48.95m },
new Product { ProductID = 3, Name = "Soccer Ball", Category = "Soccer", Price = 19.50m },
new Product { ProductID = 4, Name = "Corner Flag", Category = "Soccer", Price = 34.95m }
};
// create an indexer
ILookup<int, Product> lookup = (Lookup<int,Product>) products.ToLookup(p => p.ProductID, p => p);
Product newProduct = new Product { ProductID = 5, Name = "Basketball", Category = "Basketball", Price = 120.15m };
lookup = lookup.SelectMany(l => l)
.Concat(new[] { newProduct })
.ToLookup(l => l.ProductID, l=>l);
foreach (IGrouping<int, Product> packageGroup in lookup)
{
// Print the key value of the IGrouping.
output.WriteLine("ProductID Key {0}",packageGroup.Key);
// Iterate over each value in the IGrouping and print its value.
foreach (Product product in packageGroup)
output.WriteLine("Name {0}", product.Name);
}
Assert.Equal(lookup.Count(), 5);
}
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
}
Output:
ProductID Key 1
Name Kayak
ProductID Key 2
Name Lifejacket
ProductID Key 3
Name Soccer Ball
ProductID Key 4
Name Corner Flag
ProductID Key 5
Name Basketball