Linq-to-objects: creating a two-level hierarchy from a flat source - c#

Let's say I have this simple structure
class FooDefinition
{
public FooDefinition Parent { get; set; }
}
class Foo
{
public FooDefinition Definition { get; set; }
}
class Bar
{
public ICollection<Foo> Foos { get; set; }
}
A Bar has a list of Foos which can be simple (no parent/child relationships) or nested just one level (i.e. a parent Foo has many child Foos). As can be seen here, the relationships are specified in the FooDefinition, not the Foo itself.
What I need to do is generate a list of Foos properly grouped by this hierarchy. Consider the following source data:
var simpleDefinition = new FooDefinition();
var parentDefinition = new FooDefinition();
var childDefinition = new FooDefinition { Parent = parentDefinition };
var bar = new Bar { Foos = new[]
{
new Foo { Definition = simpleDefinition },
new Foo { Definition = parentDefinition },
new Foo { Definition = childDefinition }
}};
I'd like to get a collection of top-level items with their chilren. An adequate data structure would probably be IEnumerable<IGrouping<Foo, Foo>>.
The result would look like:
Item 1 (simple)
Item 2 (parent)
Item 3 (child)
And of course I'd like to do this with a purely-functional Linq query. I do lots of these, but my brain seems to be stuck today.

bar.Foos.Where(x => x.Definition.Parent == null)
.Select(x => Tuple.Create(x,
bar.Foos.Where(c => c.Definition
.Parent == x.Definition
)));
This will return an IEnumerable<Tuple<Foo, IEnumerable<Foo>>>, where Item2 of the Tuple contains the children for the parent in Item1. For your example, this returns two Tuples:
Item1 = simpleDefinition and Item2 containing an empty enumerable
Item1 = parentDefinition and Item2 containing an enumerable which contains childDefinition
There might be a more elegant or faster way, but I couldn't come up with it...
Oh well, I contradict my own comment a little bit with this, but it is possible with GroupBy - at least nearly:
bar.Foos.Where(x => x.Definition.Parent == null)
.GroupBy(x => x,
x => bar.Foos.Where(c => c.Definition.Parent == x.Definition));
This will return an IEnumerable<IGrouping<Foo, IEnumerable<Foo>>>.
Update:
I wanted to know, if the solution you wanted is possible at all.
Yes, it is:
bar.Foos.Where(x => x.Definition.Parent != null)
.GroupBy(x => bar.Foos.Where(y => y.Definition == x.Definition.Parent)
.Single(),
x => x)
.Union(bar.Foos.Where(x => x.Definition.Parent == null &&
!bar.Foos.Any(c => c.Definition.Parent ==
x.Definition))
.GroupBy(x => x, x => (Foo)null));
But I really don't want to know the big O of this and it really shouldn't be used ;-)

If you add a class and a method, you can get to IEnumerable<IGrouping<Foo,Foo>>.
class FooRelation{
public Foo Parent {get; set;}
public Foo Child {get; set;}
}
static IEnumerable<FooRelation> GetChildren(Bar source, Foo parent){
var children = source.Foos.Where(c => c.Definition.Parent == parent.Definition);
if(children.Any())
return children.Select(c => new FooRelation{Parent = parent, Child = c});
return new FooRelation[]{new FooRelation{Parent = parent}};
}
You might even be able to fold that static method into this query...but it would get messy:
var r = bar.Foos.Where(x => x.Definition.Parent == null)
.SelectMany(x => GetChildren(bar, x))
.GroupBy(fr => fr.Parent, fr => fr.Child);

Related

Add text to all properties in all items of a list without manual multiple loops

I have a List of MyClass
public class MyClass
{
public string Title { get; set; }
public string Name { get; set; }
}
var records = new List<MyClass>
{
new MyClass { Title = "Mr", Name = "Bob" },
new MyClass { Title = "Dr", Name = "Jon" },
};
I need to add "" around each value in each property of the class, and each item in the list. So the actual content of each property contains a " at the start and end.
Is there a way I can add this without having to loop through each property & item and do it all manually. A way using Linq perhaps?
I can't find anything on Google, I'm not even really sure what to search for.
Thanks
Yes, simply map the transformation:
var transformedRecords =
records.Select(c =>
new MyClass
{
Title = $"\"{c.Title}\"",
Name = $"\"{c.Name}\""
});
Now, if what you want is a way to edit all string properties without having to manually change them one by one, then the only way is with reflection:
static void Transform<T>(T t)
where T: class
{
if (ReferenceEquals(t, null))
throw new ArgumentNullException(nameof(t));
var propertiesToEdit =
typeof(T).GetProperties(BindingFlags.Public |
BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(string)
p.CanWrite &&
p.CanRead);
foreach (var p in propertiesToEdit)
{
p.SetValue(t, $"\"{p.GetValue(t)}\"");
}
}
But now you've got a method that mutates the object that is passed into it. You should never use mutating methods with LINQ, unexpected behaviors are to be expected.
So you are better of just simply iterating manually:
foreach (var r in records)
Transform(r);
And now your records list will contain the mutated items.
Of course, you don't need to stop at string, you can make this quite a bit more general with very little additional cost:
static void Transform<T, Q>(T t, Func<Q, Q> transform)
where T: class
{
if (ReferenceEquals(t, null))
throw new ArgumentNullException(nameof(t));
var propertiesToEdit =
typeof(T).GetProperties(BindingFlags.Public |
BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(Q)
p.CanWrite &&
p.CanRead);
foreach (var p in propertiesToEdit)
{
p.SetValue(t, transform((Q)p.GetValue(t)));
}
}
And now you'd use it like this:
foreach (var r in records)
Transform(r, (string s) => $"\"{s}\"");
The class constraint is important here because this will fail with value types; c# defaults to pass by value semantics so the method will mutate copies, not the original instances in the list.
Without using a loop I believe this is what you seek:
records.ForEach(e =>
{
e.Name = $"\"{e.Name}\"";
e.Title = $"\"{e.Title}\"";
});

c# - query nested type with LINQ

I have a model that looks like the following:
public class MyType{
public string Id {get;set;}
public string Name{get;set;}
public List<MyType> Children{get;set;}
}
and in my data I have just two level data, meaning my objects will look like:
{
MyType{"1","firstParent",
{
MyType{"2","firstChild",null},
MyType{"3","secondChild",null}}
},
MyType{"4","secondParent",
{
MyType{"5","firstChild",null},
MyType{"6","secondChild",null}}
}
}
How do I query to get MyType object with a specific Id where it might be a parent or child?
The following will return only parents.
collection.FirstOrDefault(c => c.id==id)
You can use Any with a recursive local function to find objects on any level (your data structure would seem to indicate a deeper level is possible)
bool hasIdOrChildren(MyType t, string localId)
{
return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
}
collection.FirstOrDefault(c => hasIdOrChildren(c, id));
Or using pre C#7 syntax:
Func<MyType, string, bool> hasIdOrChildren = null;
hasIdOrChildren = (MyType t, string localId) =>
{
return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
};
collection.FirstOrDefault(c => hasIdOrChildren(c, id));
If you are only interested in one level, you can drop the reclusiveness:
collection.FirstOrDefault(c => c.Id == id || (c.Children != null && c.Children.Any(o => o.Id == id)));
Edit
The code above gives the parent if any child has the id, you can also flatten the whole tree structure using SelectMany also with a recursive function:
IEnumerable<MyType> flattenTree(MyType t)
{
if(t.Children == null)
{
return new[] { t };
}
return new[] { t }
.Concat(t.Children.SelectMany(flattenTree));
};
collection
.SelectMany(flattenTree)
.FirstOrDefault(c => c.Id == id);
This method can be useful for any type of processing where you need to flatten the tree.
You could build a list of all MyType including children and then query on it like this :
collection.SelectMany(c => c.Children).Concat(collection).Where(c => c.id == id)
I think you're looking for
var flattenedList = IEnumerable.SelectMany(i => i.ItemsInList);
This flattens the list and gives back one list with all items in it.
In your case you need to select
collection.SelectMany(c => c.Type).Concat(collection).Where(item => item.Id == 5);
MSDN
You still got the childs in your joined parents here, but you can still erase them or ignore them.
I think, you should flatten collection using SelectMany method, then use FirstOrDefault to get element by id:
MyType selected = collection
.SelectMany(obj => new MyType[] {obj, obj.NestedList})
.FirstOrDefault(obj => obj.id == id);

Pass property getter to linq expression function

I'm trying to take a method and make it generic, and I'm a little stuck because the method uses Linq to look at elements. Here's the example method:
private List<int> GetListFromIDS(string ids, IEnumerable<SubSpace_Function> data)
{
if (string.IsNullOrEmpty(ids))
return null;
var list = ids
.Split(new char[] { ',' })
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => int.Parse(x.Trim()));
return data
.Where(x => list.Contains(x.Function_Id)))
.Select(x => x.Function_Id)
.ToList();
}
The parts that change are the type (SubSpace_Function) and the property to lookup Function_ID.
I know I can just change the SubSpace_Function part to T in the generic method signature, but since each type will have it's own property to lookup, I'm not sure how to 'pass' in something like Function_Id.
It's pretty easy to do with Func:
private List<int> GetListFromIDS<T>(string ids, IEnumerable<T> data, Func<T, IEnumerable<int>, bool> filterExpression, Func<T, int> selectExpression)
{
if (string.IsNullOrEmpty(ids))
return null;
var list = ids
.Split(',') // simplify
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => int.Parse(x.Trim()));
return data
.Where(x => filterExpression(x, list))
.Select(selectExpression)
.ToList();
}
And call using:
var data = GetListFromIDS<SubSpace_Function>(
"123,123,123",
someList,
(x, list) => list.Contains(x.Function_Id),
x => x.Function_Id);
Another way is to call the select Func inline:
private List<int> GetListFromIDS<T>(string ids, IEnumerable<T> data, Func<T, int> selectExpression)
{
if (string.IsNullOrEmpty(ids))
return null;
var list = ids
.Split(',') // simplify
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => int.Parse(x.Trim()));
return data
.Where(x => list.Contains(selectExpression(x)))
.Select(selectExpression)
.ToList();
}
And call using:
var data = GetListFromIDS<SubSpace_Function>(
"123,123,123",
someList,
x => x.Function_Id);
I know this focused on generics, but I took the approach of using an interface instead:
interface ISubSpaceFunction
{
int FunctionId { get; }
}
class Foo : ISubSpaceFunction
{
public int FunctionId => FooMethodForFunctionId();
private int FooMethodForFunctionId()
{
//do foo function id stuff
throw new NotImplementedException();//so it compiles
}
}
class Bar : ISubSpaceFunction
{
public int FunctionId => BarMethodForFunctionId();
private int BarMethodForFunctionId()
{
//do bar function id stuff
throw new NotImplementedException();//so it compiles
}
}
static class MyClass
{
private static List<int> GetListFromIds(string idsString, IEnumerable<ISubSpaceFunction> subSpaceFunctions)
{
var ids = string.IsNullOrWhiteSpace(idsString) ?
Enumerable.Empty<int>() :
idsString.Split(new[] { ',' })
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => x.Trim())
.Select(int.Parse);
var idSet = new HashSet<int>(ids);
return subSpaceFunctions.Select(ssf => ssf.FunctionId)
.Where(ids.Contains)
.ToList();
}
}
class Example
{
public void Test()
{
string ids = "1, 2, 3, 4, 5";
var subSpaceFunctions = new ISubSpaceFunction[] { new Foo(), new Bar() };
var results = MyClass.GetListFromIds(ids, subSpaceFunctions);
}
}
My attitude on this and related matters is that the code to get the Property value for each particular type has to go somewhere, so it might as well go in the Type's class. This ensures that if the Property's value is needed elsewhere, there is no duplication. This also allows for mixing multiple types that satisfy ISubSpaceFunction, as is done in the example, and you could easily have the interface also specify some common method to be used elsewhere.
I also prefer returning empty collections over null when writing these kinds of LINQ based transformation methods in order to minimize null checking "down the pipeline," but a "fail fast" use case may call for a null return value.

Sort a List<T> by another List<T>

My model is:
class Person
{
public int Id {get; set; }
public string Name {get; set; }
}
I have two collections. And I would like to sort toBeSortedList like etalonList:
List<Person> etalonList = new List<Person>()
{
new Person() { Id=10, Name="Jon"},
new Person() { Id=4, Name="Ben"},
new Person() { Id=11, Name="Magnus"},
new Person() { Id=8, Name="Joseph"},
};
List<Person> toBeSortedList = new List<Person>()
{
new Person() { Id=11, Name="Magnus"},
new Person() { Id=4, Name="Ben"},
new Person() { Id=10, Name="Jon"},
new Person() { Id=8, Name="Joseph"},
};
I've tried:
var orderedByIdList = tobeSortedList.OrderBy(x => etalonList.IndexOf(x.Id));
But I've met a such error:
cannot convert from 'int' to 'SOConsoleApplication.Person'
Maybe you have another suggestions?
I would first create a dictionary from etalonList in order to speed up sorting:
int index;
var etalonDictionary = etalonList.ToDictionary(k => k.Id, v => index++);
Then find back the ID from the dictionary and use that for sorting:
var sortedList = toBeSortedList.OrderBy(x => etalonDictionary[x.Id]).ToList();
List.IndexOf takes the object as argument and returns the index of it. You are passing an int-value which is the Id. That doesn't compile.
You could override Equals+GethashCode and pass x instead of x.Id. But I would prefer List.FindIndex in this case:
var orderedByIdList = toBeSortedList
.OrderBy(x => etalonList.FindIndex(p => p.Id == x.Id));
Here's the override Equals approach which enables to use IndexOf:
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
if(ReferenceEquals(obj, this))
return true;
Person other = obj as Person;
if (other == null)
return false;
return other.Id == this.Id;
}
public override int GetHashCode()
{
return Id;
}
}
Now this works too:
var orderedByIdList = toBeSortedList
.OrderBy(x => etalonList.IndexOf(x));
I'm guessing that some values in etalonList are not present in toBeSortedList because in all other cases the question makes no sense:
toBeSortedList has elements not included in etalonList. In this case the problem is underspecified; how would you order these new list members?
toBeSortedList contains exactly the same members etalonList has. In that case simply return etalonList or duplicate it.
A naive, but simple way to order toBeSortedList is the following (note that I am assuming that case 1 is not possible):
static IEnumerable<T> OrderBy(this IEnumerable<T> list, IEnumerable<T> guideList)
{
foreach (var member in guideList)
{
if (toBeSortedList.Contains(member))
yield return member;
}
}
var orderedList = toBeSortedList.OrderBy(etalonList).ToList();
Doesn't perform all that well but if lists are not very long, it should do.
You don't need regular sort in this case at all. You can simply join the etalon with target and project the target elements. If the etalon does not contain all the targets, you can concatenate the non existing items either at the beginning or at the end of the sorted sequence, eventually applying some additional order.
In both cases, you'll end up with a fast O(N) time complexity operation.
Here is the LINQ that puts the non existing items at the end:
var orderedByIdList =
(from a in etalonList join b in toBeSortedList on a.Id equals b.Id select b)
.Concat
(from a in toBeSortedList join b in etalonList on a.Id equals b.Id into b where !b.Any() select a)
.ToList();

Best Comparison Algorithm using Entity Framework

I was wondering what was the best approach to compare multiple objects that are created and having the state of the objects changed to Inactive (Deleted), while creating history and dependencies.
This also means im comparing past and present objects inside a relational table (MarketCookies).
Id | CookieID | MarketID
The ugly solution i found was calculating how many objects had i changed.
For this purpose lets call the items of the Past: ListP
And the new items: ListF
I divided this method into three steps:
1 - Count both lists;
2 - Find the objects of ListP that are not present in List F and change their state to Inactive and update them;
3 - Create the new Objects and save them.
But this code is very difficult to maintain.. How can i make an easy code to maintain and keep the functionality?
Market Modal:
public class Market()
{
public ICollection<Cookie> Cookies {get; set;}
}
Cookie Modal:
public class Cookie()
{
public int Id {get;set;}
//Foreign Key
public int CookieID {get;set}
//Foreign Key
public int MarketID {get;set;}
}
Code:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
int ListPCount = ListP.Count();
int ListFCount = ListF.Count();
if(ListPCount > ListFCount)
{
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete the Object
}
});
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Object
}
});
}
else if(ListPCount < ListFCount)
{
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Objects
}
});
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete Objects
}
});
}
else if(ListPCount == ListFCount)
{
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete Objects
}
});
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Objects
}
});
}
}
Without a good, minimal, complete code example that clearly illustrates the question, it's hard to know for sure what even a good implementation would look like, never mind "the best". But, based on your description, it seems like the LINQ Except() method would actually serve your needs reasonably well. For example:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
foreach (var item in ListP.Except(ListF))
{
// set to inactive
}
foreach (var item in ListF.Except(ListP))
{
// create new object
}
}
This of course assumes that your objects have overridden Equals() and GetHashCode(). If not, you can provide your own implementation of IEqualityComparer<T> for the above. For example:
// General-purpose equality comparer implementation for convenience.
// Rather than declaring a new class for each time you want an
// IEqualityComparer<T>, just pass this class appropriate delegates
// to define the actual implementation desired.
class GeneralEqualityComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _equals;
private readonly Func<T, int> _getHashCode;
public GeneralEqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
{
_equals = equals;
_getHashCode = getHashCode;
}
public bool Equals(T t1, T t2)
{
return _equals(t1, t2);
}
public int GetHashCode(T t)
{
return _getHashCode(t);
}
}
Used like this:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
IEqualityComparer<Cookie> comparer = new GeneralEqualityComparer<Cookie>(
(t1, t2) => t1.Id == t2.Id, t => t.Id.GetHashCode());
foreach (var item in ListP.Except(ListF, comparer))
{
// set to inactive
}
foreach (var item in ListF.Except(ListP, comparer))
{
// create new object
}
}

Categories

Resources