How to get distinct values from nested lists of objects? - c#

I have the following problem:
foo f = new foo();
// ...
f.bars.SelectMany(m => m.qux.GroupBy(x => x.hash))
.Select(group => group.First())
.Sum(s => s.size);
This returns the sum of the total size property for qux within a single bar instance. Adding another qux object with an existing hash value in another bar is ignored so its size value is counted in the computed sum. (Even tho the same qux exists in another bar object).
How can I tweak my query to make sure all objects having the same hash are calculated once?
The data structure would look like this:
class foo
{
public List<bar> bars = new List<bar>();
}
class bar
{
public List<qux> qux = new List<qux>();
}
class qux
{
public string hash { get; set; }
public int size { get; set; }
}

Judging from your data structure, here is what you need:
int sizeSum =
bars.SelectMany(m => m.qux)
.GroupBy(q => q.hash)
.Select(g => g.First())
.Sum(q => q.size);
The difference with your initial solution is that we first get a unified list with .SelectMany(), and only then do we group it by hash.

Related

Automapper is it supported to map to self and flatter / perform maps on same object(s)

I have a tree of objects. I would like to update the objects with some default values and flattening some propertied also.
However, I do not want to create a new object, just update the existing tree.
So I thought. Well, I can do it with a mass of logic and recursion. Or I could try it with the auto mapper. Basically map to self. And automapper might update the destination with the source. and perform the flatterning:
Source/destination types
public class Foo
{
public bool SomeBool { get; set; }
public IEnumerable<Bar> Bars { get; set; }
}
public class Bar
{
public bool SomeBoolWithDefalting { get; set; }
}
Mapping configuration
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Foo, Foo>();
cfg.CreateMap<Bar, Bar>()
.ForMember(dest => dest.SomeBoolWithDefalting, opt=>opt.MapFrom(src=>true));
});
var mapper = config.CreateMapper();
var foo = new Foo
{
Bars = new List<Bar> { new Bar() }
};
mapper.Map<Foo, Foo>(foo, foo);
Version:
9.0.0
Expected behavior
I expected that Bars get updated with the default value. And foo and bars is the same object as before
Actual behavior
But all I got back is empty bars.
if I:
var newFoo = mapper.Map<Foo, Foo>(foo);
Then I get a new foo, with the bars updated.
I know auto mapper should be used to make new objects. But isn't. Is use case valid? performing flattering on a destination only?
Steps to reproduce
class Program
{
static void Main(string[] args)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Foo, Foo>();
cfg.CreateMap<Bar, Bar>()
.ForMember(dest => dest.SomeBoolWithDefalting, opt => opt.MapFrom(src => true));
});
var mapper = config.CreateMapper();
var foo = new Foo
{
Bars = new List<Bar> { new Bar() }
};
var newFoo = mapper.Map<Foo, Foo>(foo); //good bars, but new objects
mapper.Map<Foo, Foo>(foo, foo); //same foo, but no bars
Console.WriteLine(foo.Bars.First().SomeBoolWithDefalting);
}
}
Short answer: When mapping to an existing collection, AutoMapper clears the destination collection first.
Explanation
Let's add a Name property to your Bar class:
public class Bar
{
public string Name { get; set; }
public bool SomeBoolWithDefalting { get; set; }
}
Then run this mapping snippet below:
var newFoo = new Foo
{
Bars = new List<Bar>
{
new Bar
{
Name = "Bar that will be mapped.",
},
},
};
var notEmptyFoo = new Foo()
{
Bars = new List<Bar>
{
new Bar
{
Name = "Bar that will be removed.",
}
},
};
Console.WriteLine(notEmptyFoo.Bars.First().Name);
mapper.Map<Foo, Foo>(newFoo, notEmptyFoo);
Console.WriteLine(notEmptyFoo.Bars.First().Name);
Code above maps from Foo with one already existing Bar to another Foo also with already existing Bar. But AutoMapper will remove all Bars from destination Foo (named in the snippet notEmptyFoo) just before the mapping process begin.
The console output will show:
Bar that will be removed.
Bar that will be mapped.
So, when you are mapping from and to the same Foo, not only the destination Foo bars are being cleared before the mapping, but also the source Foo as well as it's the same and one Foo object! So after the clearing there are no bars in the source Foo to map from, as it just got cleared.
If you would like to preserve elements of destination collection, take a look at AutoMapper.Collection project. There you can set an EqualityComparison telling AutoMapper how to recognize the same elements when mapping between the collections.

Binary Search with List in c#

I am using c# WPF for developing a Windows Application.
The application requires a class as follows
public class Limits
{
public String col1
{
get;
set;
}
public String col2
{
get;
set;
}
public String col3
{
get;
set;
}
}
I am using a List to Store Objects like:-
List myList<Limits> = new List<Limits>();
"myList" has around 15000 Objects.
Now, I want to search this myList for a particular attribute.
Eg: I want to find out the object that has col1 set as "abc".
How can I use Binary Search for this problem?
First of all, the list has to be sorted on the col1 property for you to be able to use binary search at all.
You would need a comparer that compares the col1 property:
public class LimitsComparer : IComparer<Limits> {
public int Compare(Limits x, Limits y) {
return x.col1.CompareTo(y.col1);
}
}
Then you can use that to do the binary search:
int index = myList.BinarySearch(new Limits { col1 = "abc" }, new LimitsComparer());
The index returned is:
The zero-based index of item in the sorted List, if item is found;
otherwise, a negative number that is the bitwise complement of the
index of the next element that is larger than item or, if there is no
larger element, the bitwise complement of Count.
You can also use the Where method to get the objects that has that property:
List<Limits> result = myList.Where(x => x.col1 == "abc").ToList();
Although that is not quite as efficient, you should still consider if that is a better solution as it's easier to implement and gives a result that is easier to handle. Also (and this might be more important), it works even if the list isn't sorted on col1.
You could use somthing like this.
myList.Where(i => i.col1 == "abc").ToList();
Use a dictionary where the keys are stored in a hash table. Linq will create the cdictionary easily.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication41
{
class Program
{
static void Main(string[] args)
{
List<Limits> myList = new List<Limits>();
//dictionary with unique keys
Dictionary<string, Limits> dict1 = myList.AsEnumerable()
.GroupBy(x => x.col2, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
//dictionary with keys having multiple values
Dictionary<string, List<Limits>> dict2 = myList.AsEnumerable()
.GroupBy(x => x.col2, y => y)
.ToDictionary(x => x.Key, y => y.ToList());
Limits abc = dict1["abc"];
}
}
public class Limits
{
public String col1 { get; set; }
public String col2 { get; set; }
public String col3 { get; set; }
}
}
unless you explicitly want to use binary search, you should use the standard Linq functions available to you. Unless your list is already sorted, this might be more efficient than binary sort.
var myList = new List<Limits> {....}
var entry = myList.Where(l => l.col1== "abc").FirstOrDefault();
if(entry == null)
{ // no match found }
If you really want binary search, ref Can LINQ use binary search when the collection is ordered?

LINQ - Getting all child records from all parents

I have two models:
class Foo
{
public List<Bar> Bars { get; set; }
}
class Bar
{
public int Value { get; set; }
}
Having an instance of List<Foo>, how can I get all Value using a LINQ query?
Thank you all
SelectMany is normally the way to flatten hierarchies, so:
var values = myList.SelectMany(foo => foo.Bar)
.Select(bar => bar.Value);
The SelectMany will give you an IEnumerable<Bar>, and then the Select projects that sequence of Bar objects to the Value property of each, returning an IEnumerable<int>.
As a query expression, this would be:
var values = from foo in myList
from bar in foo.Bar
select bar.Value;
I suggest you to change List<Bar> property name to Bars.
And firstly use SelectMany(). It projects each element of a sequence to an IEnumerable<T> and flattens the resulting sequences into one sequence. And then use Select() to project each element of a new sequence as you wish.
var result = myList.SelectMany(x => x.Bars).Select(x => x.Value).ToList();
Use SelectMany instead of Select
var result = LIST1.SelectMany(x => x.LIST2.Select(y => y.Value)).Tolist();

Limit queried columns based on list set in runtime

I have the following query:
// Type T is constrained to a class that contains "ID" property
// propertiesToQuery is a list constructed based on type T
var set = AppContext.Set<T>();
var result = set.SelectMany(x => propertiesToQuery.Select(p => new { x.ID, Value = x.GetType().GetProperty(p.Name).GetValue(x) })
.Where(p => p.Value != null)
.Select(p => new SearchIndexItem
{
Key = p.Value.ToString(),
Url = Url.Action("Edit", type.Name, new { p.ID }),
Type = type
}));
Now because linq to entities does not allow to use PropertyInfo in queries, I need to run a ToList() on the set in order to execute the query on the db first and then perform the desired SelectMany().
This queries more than it needs to from the database which will be a problem when there's a lot of data (queried columns are of type string, others can be blobs and these are the ones I don't want to pull the data from)
So the question is how can I limit the columns queried from the db based on a list constructed at runtime?
I was trying to create an expression tree, and pass it to the Select() method on the set, but the problem was with creating the anonymous type, which can be different depenging on type T.
Your observation here:
the problem was with creating the anonymous type, which can be different depenging on type T
is accurate; it is hugely problematic to construct the result columns at runtime. The only simply way to do that is to make it look like you are populating a projection of a type that has all the members, for example the equivalent of:
// where our list is "Foo", "Bar":
x => new SomeType {
Foo = x.Foo,
Bar = x.Bar
// but other SomeType properties exist, but are't mapped
}
The obvious contender would be the entity type, so you are partially mapping a set of Customer rows to Customer objects - but most ORMs won't let you do that: if the projection is an entity type they want the entire type (i.e. x => x). You might be able to create a second version of the entity type that is a regular POCO/DTO but isn't part of the entity model, i.e.
Customer x => new CustomerDto {
Foo = x.Foo,
Bar = x.Bar
}
which you can do as part of Expression.MemberInit at runtime. Example:
class Foo
{
public string A { get; set; }
public int B { get; set; }
public DateTime C { get; set; }
}
class FooDto
{
public string A { get; set; }
public int B { get; set; }
public DateTime C { get; set; }
}
class Program
{
static void Main(string[] args)
{
var data = new[] { new Foo { A = "a", B = 1, C = DateTime.Now}}
.AsQueryable();
var mapped = PartialMap<Foo, FooDto>(data, "A", "C").ToList();
}
static IQueryable<TTo> PartialMap<TFrom, TTo>(
IQueryable<TFrom> source, params string[] members)
{
var p = Expression.Parameter(typeof(TFrom));
var body = Expression.MemberInit(Expression.New(typeof(TTo)),
from member in members
select (MemberBinding)Expression.Bind(
typeof(TTo).GetMember(member).Single(),
Expression.PropertyOrField(p, member))
);
return source.Select(Expression.Lambda<Func<TFrom, TTo>>(body, p));
}
}
in the output, A and C have values, but B does not.

LINQ - Select correct values from nested collection

Consider the following class hierarchy:
public class Foo
{
public string Name { get; set; }
public int Value { get; set; }
}
public class Bar
{
public string Name { get; set; }
public IEnumerable<Foo> TheFoo { get; set; }
}
public class Host
{
public void Go()
{
IEnumerable<Bar> allBar = //Build up some large list
//Get Dictionary<Bar, Foo> with max foo value
}
}
What I would like to do using Linq2Objects is to get an KeyValuePair where for each Bar in the allBBar collection we select the Foo with the maximum Value property. Can this be done easily in a single LINQ statement?
Sure, although my preferred solution uses MaxBy from MoreLINQ:
var query = allBar.ToDictionary(x => x, // Key
x => x.TheFoo.MaxBy(f => f.Value));
Note that this will go pear-shaped if TheFoo is empty for any Bar instance.
Another way using Aggregate instead of OrderBy so that figuring out the max Foo is O(n) instead of O(n log n):
var query = allBar.ToDictionary(
bar => bar,
bar => bar.TheFoo.Aggregate(
null,
(max, foo) => (max == null || foo.Value > max.Value) ? foo : max));
just to add to Jon's comment about MaxBy going pear shaped if you have no foos, you could do an OrderByDescending and then use FirstOrDefault to get at the Max element. If the collection is empty it'd just return null instead of going "pear shaped".
var foobars = bars.ToDictionary(bar => bar,
bar => bar.TheFoo.OrderByDescending(foo => foo.Value).FirstOrDefault());
I don't think this wouldn't be as efficient as MaxBy, but it'd be more robust in the case of an empty collection.

Categories

Resources