Hierarchical structure iteration and LINQ - linq

Assume that we have class
public class RMenuItem
{
public List<RMenuItem> ChildrenItems { get; }
public decimal OperationID { get; }
public string Name { get; }
}
as you can see - each menuitem could have children items - as usual in menu.
My task is to iterate through each items of this list and apply some action to it. Classical decision is to write recursive iteration. But I'm interesting if LINQ could make my task easier? For example, I suppose that we can write query that can get flat list of objects, which i can iterate simply with foreach. But my attempts in this way weren't successful yet.
So any help appreciated!

It's possible:
public void PrintAllNames(RMenuItem rootItem)
{
Action<RMenuItem> print = null;
print = m =>
{
Console.WriteLine(m.Name);
m.ChildrenItems.ForEach(print);
};
print(rootItem);
}
Notice how it's necessary to declare print so that print can use itself. This is directly comparable to a recursive method, which I'd rather use:
public void PrintAllNames(RMenuItem rootItem)
{
Console.WriteLine(rootItem.Name);
rootItem.ChildrenItems.ForEach(PrintAllNames);
}
(although for a more complex situation, maybe the functional solution would make the most sense)

I suggest 2 ways of achieving this. You can opt with an utility method to get all the items or you can implement the Visitor Pattern, though it implies changing the RMenuItem class.
Utility method:
static IEnumerable<RMenuItem> GetAllMenuItems(IList<RMenuItem> items)
{
if (items == null)
throw new ArgumentNullException("items");
Queue<RMenuItem> queue = new Queue<RMenuItem>(items);
while (queue.Count > 0)
{
var item = queue.Dequeue();
if (item.ChildrenItems != null)
{
foreach (var child in item.ChildrenItems)
{
queue.Enqueue(child);
}
}
yield return item;
}
}
I prefer an imperative way to a recursive because we can use iterator blocks.
Visitor Pattern:
public interface IRMenuItemVisitor
{
void Visit(RMenuItem item);
}
public class PrintRMenuItemVisitor : IRMenuItemVisitor
{
public void Visit(RMenuItem item)
{
Console.WriteLine(item);
}
}
public interface IRMenuItem
{
void Accept(IRMenuItemVisitor visitor);
}
public class RMenuItem : IRMenuItem
{
// ...
public void Accept(IRMenuItemVisitor visitor)
{
visitor.Visit(this);
if (ChildrenItems != null)
{
foreach (var item in ChildrenItems)
{
item.Accept(visitor);
}
}
}
}
Usage:
RMenuItem m1 = new RMenuItem
{
Name = "M1",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M11" },
new RMenuItem {
Name = "M12",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M121" },
new RMenuItem { Name = "M122" }
}
}
}
};
RMenuItem m2 = new RMenuItem
{
Name = "M2",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M21" },
new RMenuItem { Name = "M22" },
new RMenuItem { Name = "M23" }
}
};
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in GetAllMenuItems(menus))
{
Console.WriteLine(menu);
}
// or
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in menus)
{
menu.Accept(new PrintRMenuItemVisitor());
}

You could difine a Flatten method in your class (or as an extension if you prefer) like this
public IEnumerable<RMenuItem> Flatten()
{
foreach (var item in ChildrenItems)
{
yield return item;
}
return ChildrenItems.SelectMany(item => item.Flatten());
}
then doing somthing with each elements will be as simple as
RMenuItem rootItem ;
// do somthing with the root item
foreach (var item in rootItem.Flatten())
{
// do somthing
}

Indeed you can do that using LINQ, SelectMany flats out the list, just some example
menuItemsList.SelectMany(x => x.ChildrenItems).Where(c => c.someChildProperty);
Thanks
Edit:
In response to the comments, I was just giving an example of SelectMany previously. Thanks for pointing out.
menuItemsList.SelectMany(x => x.ChildrenItems.Select(p => p)).Where(c => c.someChildProperty);
OR something like this
menuItemsList.SelectMany(x => x.ChildrenItems).Select(p => p).Where(c => c.someChildProperty);
Edit2
Ahh .. now I understood what you want ..
We can just slightly modify my above query to do what you want
menuItemsList
.SelectMany(x => { //do something with x like printing it
x.ChildrenItems
})
.Select(p => { // do something with p like printing it
p
});
Basically you can do what you want the element inside the {}
Thanks

Related

keeping track of previous elements in foreach loop

Lets say I have a list of asteroid objects like so:
9_Amphitrite
24_Themis
259_Aletheia
31_Euphrosyne
511_Davida
87_Sylvia
9_Metis
41_Daphne
Each asteroid has a title, a StartRoationPeriod, and a EndRoationPeriod.
I need to concatenate their names based on how close the current asteroid StartRoationPeriod and previous asteroid EndRoationPeriod are to an orbital constant and then spit out the concatenated title.
So with the above list, the final objects may look like this:
9_Amphitrite
24_Themis;259_Aletheia
31_Euphrosyne;511_Davida;87_Sylvia
9_Metis
41_Daphne
This requires me to keep track of both the current and previous asteroids.
I started to write the loop, but I'm unsure of where or even how to check the current asteroids start rotation period against the previous asteroids end rotation period...basically, it just gets messy fast...
string asteroid_title = string.Empty;
Asteroid prev_asteroid = null;
foreach (var asteroid in SolarSystem)
{
if (prev_asteroid != null)
{
if (asteroid.StartRoationPeriod + OrbitalConstant >= prev_asteroid.EndRoationPeriod)
{
asteroid_title = asteroid_title + asteroid.Title;
} else {
asteroid_title = asteroid.Title;
yield return CreateTitle();
}
}
prev_evt = evt;
}
I think this should work for you (If aggregate looks too complex try to convert it to a foreach,it's easy)
using System;
using System.Collections.Generic;
using System.Linq;
namespace Program
{
class Asteroid
{
public int EndRoationPeriod { get; internal set; }
public string Name { get; internal set; }
public int StartRoationPeriod { get; internal set; }
}
class AsteroidGroup
{
public int EndRoationPeriod { get; internal set; }
public string Names { get; internal set; }
}
internal class Program
{
private static void Main(string[] args)
{
int OrbitalConstant = 10;
List<Asteroid> SolarSystem = new List<Asteroid>()
{
new Asteroid() { Name= "9_Amphitrite" ,StartRoationPeriod=10 ,EndRoationPeriod=50},
new Asteroid() { Name= "24_Themis" ,StartRoationPeriod=45,EndRoationPeriod=100},
new Asteroid() { Name= "259_Aletheia",StartRoationPeriod=40 ,EndRoationPeriod=150},
new Asteroid() { Name= "31_Euphrosyne" ,StartRoationPeriod=60,EndRoationPeriod=200},
new Asteroid() { Name= "511_Davida" ,StartRoationPeriod=195,EndRoationPeriod=250},
new Asteroid() { Name= "87_Sylvia" ,StartRoationPeriod=90,EndRoationPeriod=300},
new Asteroid() { Name= "9_Metis" ,StartRoationPeriod=100,EndRoationPeriod=350},
new Asteroid() { Name= "41_Daphne" ,StartRoationPeriod=110,EndRoationPeriod=400},
};
var result = //I skip the first element because I initialize a new list with that element in the next step
SolarSystem.Skip(1)
//The first argument of Aggregate is a new List with your first element
.Aggregate(new List<AsteroidGroup>() { new AsteroidGroup { Names = SolarSystem[0].Name, EndRoationPeriod = SolarSystem[0].EndRoationPeriod } },
//foreach item in your list this method is called,l=your list and a=the current element
//the method must return a list
(l, a) =>
{
//Now this is your algorithm
//Should be easy to undrestand
var last = l.LastOrDefault();
if (a.StartRoationPeriod + OrbitalConstant >= last.EndRoationPeriod)
{
last.Names += " " + a.Name;
last.EndRoationPeriod = a.EndRoationPeriod;
}
else
l.Add(new AsteroidGroup { Names = a.Name, EndRoationPeriod = a.EndRoationPeriod });
//Return the updated list so it can be used in the next iteration
return l;
});
A more compact solution
var result = SolarSystem
.Skip(1)
.Aggregate( SolarSystem.Take(1).ToList(),
(l, a) => (a.StartRoationPeriod + OrbitalConstant >= l[l.Count - 1].EndRoationPeriod) ?
(l.Take(l.Count - 1)).Concat(new List<Asteroid> { new Asteroid() { Name = l[l.Count - 1].Name += " " + a.Name, EndRoationPeriod = a.EndRoationPeriod } }).ToList() :
l.Concat(new List<Asteroid> { a }).ToList()
);

Initializing[Unavailable#]-failed to lazily initialize a collection, no session or session was closed

I have one class called fixture which has two properties description and date. Now i dont want to operate directly on thi stwo propert but i wanted to operate on Some collection object "Allattribute" in this example which i m feeling while in get and set of each property.
retrival is working fine but while persisting i m getting this error
Initializing[Unavailable#]-failed to lazily initialize a collection, no session or session was closed.
Code for Model and Mapper is as below:
public class Fixture : EntityBase
{
public Fixture() {
//PogObject = new List<PogObject>();
//ObjectFixtureDate = new List<Objectfixturedate>();
}
public virtual long IDPOGObject { get; set; }
private ObservableCollection<DictionaryEntry> m_allattributes = new ObservableCollection<DictionaryEntry>();
private IList<FixtureDate> mFixtureDate = new List<FixtureDate>();
private IList<FixtureDesc> mFixtureDesc = new List<FixtureDesc>();
private IList<FixtureFlag> mFixtureFlag = new List<FixtureFlag>();
public virtual IList<FixtureDate> FixtureDate
{
get
{
mFixtureDate.Clear();
foreach (var item in m_allattributes)
{
if (item.Value is FixtureDate)
{
mFixtureDate.Add((FixtureDate)item.Value);
}
}
return mFixtureDate;
}
set
{
//try
//{
if (value.Count != 0)
{
foreach (var item in value)
{
DictionaryEntry dEntry = new DictionaryEntry(item.Dictionary.DictionaryName, item);
if (!m_allattributes.Contains(dEntry))
{
m_allattributes.Add(dEntry);
}
}
}
//}
//catch (Exception ex)
//{
//}
mFixtureDate = value;
}
}
public virtual IList<FixtureDesc> FixtureDescription
{
get
{
mFixtureDesc.Clear();
foreach (var item in m_allattributes)
{
if (item.Value is FixtureDesc)
{
if (!mFixtureDesc.Contains((FixtureDesc)item.Value))
{
mFixtureDesc.Add((FixtureDesc)item.Value);
}
}
}
return mFixtureDesc;
}
set
{
if (value.Count != 0)
{
foreach (var item in value)
{
DictionaryEntry dEntry = new DictionaryEntry(item.Dictionary.DictionaryName, item);
// DictionaryEntry dEntry = new DictionaryEntry(item.DescNum, item);
if (!m_allattributes.Contains(dEntry))
{
m_allattributes.Add(dEntry);
}
}
}
mFixtureDesc = value;
}
}
}
Mapper Class:
public class FixtureMap : ClassMap {
public FixtureMap() {
Table("SPOG_ObjectFixture");
Not.LazyLoad();
Id(x => x.IDPOGObject).GeneratedBy.Foreign("PogObject");
HasMany(x => x.FixtureDescription).Cascade.AllDeleteOrphan().Inverse().Not.LazyLoad().KeyColumn("IDPOGObject").Fetch.Join();
HasMany(x => x.FixtureDate).Cascade.AllDeleteOrphan().Inverse().Not.LazyLoad().KeyColumn("IDPOGObject");
}
}

EmitMapper and List

It's the first time that I use EmitMapper.
I have a list of object ex: Customer and I would like to map this list in a ienumerable of CustomerDTO how can I do that?
Tnx
It's straightforward if you have a list and want to convert it to list of DTOs:
var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Customer, CustomerDTO>();
IEnumerable<CustomerDTO> dtos = listOfCustomer.Select(mapper.map);
The preblem is when the list is in another object, for example User and UserDTO:
class User {
public List<Customer> Customers { get; set; }
}
class UserDTO {
public IEnumerable<CustomerDTO> Customers { get; set; }
}
It seems that EmitMapper does not support conversion from List to Enumerable. A way to support it would be:
var customerMapper = ObjectMapperManager
.DefaultInstance.GetMapper<Customer, CustomerDTO>();
var mapper = ObjectMapperManager.DefaultInstance
.GetMapper<User, UserDTO>(
new DefaultMapConfig()
.ConvertUsing<List<Customer>, IEnumerable<CustomerDTO>>(
a => a.Select(customerMapper.Map))
);
This can be done creating a custom class, implementing the interface "ICustomConverterProvider" and adding a ConvertGeneric to the "DefaultMapConfig".
Looking on the source code of EmitMapper, i found a class named "ArraysConverterProvider", which is the default generic converter from ICollections to Arrays.
Adapting the code from this class to work with IEnumerable collections:
class GenericIEnumerableConverterProvider : ICustomConverterProvider
{
public CustomConverterDescriptor GetCustomConverterDescr(
Type from,
Type to,
MapConfigBaseImpl mappingConfig)
{
var tFromTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(from);
var tToTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(to);
if (tFromTypeArgs == null || tToTypeArgs == null || tFromTypeArgs.Length != 1 || tToTypeArgs.Length != 1)
{
return null;
}
var tFrom = tFromTypeArgs[0];
var tTo = tToTypeArgs[0];
if (tFrom == tTo && (tFrom.IsValueType || mappingConfig.GetRootMappingOperation(tFrom, tTo).ShallowCopy))
{
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_OneTypes<>),
ConverterClassTypeArguments = new[] { tFrom }
};
}
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_DifferentTypes<,>),
ConverterClassTypeArguments = new[] { tFrom, tTo }
};
}
}
class GenericIEnumerableConverter_DifferentTypes<TFrom, TTo> : ICustomConverter
{
private Func<TFrom, TTo> _converter;
public IEnumerable<TTo> Convert(IEnumerable<TFrom> from, object state)
{
if (from == null)
{
return null;
}
TTo[] result = new TTo[from.Count()];
int idx = 0;
foreach (var f in from)
{
result[idx++] = _converter(f);
}
return result;
}
public void Initialize(Type from, Type to, MapConfigBaseImpl mappingConfig)
{
var staticConverters = mappingConfig.GetStaticConvertersManager() ?? StaticConvertersManager.DefaultInstance;
var staticConverterMethod = staticConverters.GetStaticConverter(typeof(TFrom), typeof(TTo));
if (staticConverterMethod != null)
{
_converter = (Func<TFrom, TTo>)Delegate.CreateDelegate(
typeof(Func<TFrom, TTo>),
null,
staticConverterMethod
);
}
else
{
_subMapper = ObjectMapperManager.DefaultInstance.GetMapperImpl(typeof(TFrom), typeof(TTo), mappingConfig);
_converter = ConverterBySubmapper;
}
}
ObjectsMapperBaseImpl _subMapper;
private TTo ConverterBySubmapper(TFrom from)
{
return (TTo)_subMapper.Map(from);
}
}
class GenericIEnumerableConverter_OneTypes<T>
{
public IEnumerable<T> Convert(IEnumerable<T> from, object state)
{
if (from == null)
{
return null;
}
return from;
}
}
This code is just a copy with a minimum of adaptation as possible and can be applyed to objects with many levels of hierarchy.
You can use the above code with the following command:
new DefaultMapConfig().ConvertGeneric(
typeof(IEnumerable<>),
typeof(IEnumerable<>),
new GenericIEnumerableConverterProvider());
This saved my day and I hope to save yours too! hehehe

Compare 2 lists using linq

I'm a linq noob.... can someone please some me how to achieve this using linq... I'm trying to compare 2 lists in both directions...
internal void UpdateUserTeams(int iUserID)
{
UserTeamCollection CurrentTeams = GetUserTeams(iUserID);
UserTeamCollection UpdatedTeams = this;
foreach (UserTeam ut in CurrentTeams)
{
if(!UpdatedTeams.ContainsTeam(ut.ID))
{
RemoveTeamFromDB();
}
}
foreach (UserTeam ut in UpdatedTeams)
{
if (!CurrentTeams.ContainsTeam(ut.ID))
{
AddTeamToDB();
}
}
}
public bool ContainsTeam(int iTeamID)
{
return this.Any(t => t.ID == iTeamID);
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Linqage
{
class Program
{
static void Main(string[] args)
{
UserTeamCollection currentTeams = new UserTeamCollection()
{
new UserTeam(1),
new UserTeam(2),
new UserTeam(3),
new UserTeam(4),
new UserTeam(5)
};
UserTeamCollection updatedTeams = new UserTeamCollection()
{
new UserTeam(2),
new UserTeam(4),
new UserTeam(6),
new UserTeam(8)
};
currentTeams.Except(updatedTeams).All(u =>
{
//Console.WriteLine("Item ID: {0}",u.ID);
//RemoveFromDB()
return true;
});
updatedTeams.Except(currentTeams).All(u =>
{
//Console.WriteLine("Item ID: {0}", u.ID);
//AddToDB()
return true;
});
}
}
public class UserTeamCollection
: List<UserTeam>
{
}
//Either overwrite the GetHashCode and Equals method OR create a IComparer
public class UserTeam
{
public int ID { get; set; }
public UserTeam(int id)
{
ID = id;
}
public override bool Equals(object obj)
{
UserTeam iOther = obj as UserTeam;
if (iOther != null)
{
return this.ID == iOther.ID;
}
return false;
}
public override int GetHashCode()
{
return ID.GetHashCode();
}
}
}
So converting your initial question to an english requirement:
foreach (UserTeam ut in CurrentTeams) // for each current team
{
if(!UpdatedTeams.ContainsTeam(ut.ID)) // that is not in the updated teams list
{
RemoveTeamFromDB(); // remove it from the database
}
}
foreach (UserTeam ut in UpdatedTeams) //for each of the teams in the updated teams list
{
if (!CurrentTeams.ContainsTeam(ut.ID)) //if the current team does not contain the updated team
{
AddTeamToDB(); //add the team to the database
}
}
Therefore, you want to do:
//select all current teams that are not in updated teams list
CurrentTeam.Except(UpdatedTeams).All(team => { RemoveTeamFromDB(team); return true; });
//select all updated teams that are not in the current teams list
UpdatedTeam.Except(CurrentTeams).All(team => { AddTeamToDB(team); return true; });
Make sure your UserTeam object has proper overrides for the Equals and GetHashCode methods, so that comparison between two UserTeams is accurate :)
You would normally use Enumerable.Except both ways to get the differences. Then add and remove as needed.
var addedTeams = UpdatedTeams.Except(CurrentTeams);
var removedTeams = CurrentTeams.Except(UpdatedTeams);
You're trying to get the outer parts from a full outer join. Here's a rough way to achieve that.
ILookup<int, UserTeam> currentLookup = CurrentTeams
.ToLookup(ut => ut.ID);
ILookup<int, UserTeam> updatedLookup = UpdatedTeams
.ToLookup(ut => ut.ID);
List<int> teamIds = CurrentTeams.Select(ut => ut.ID)
.Concat(UpdatedTeams.Select(ut => ut.ID))
.Distinct()
.ToList();
ILookup<string, UserTeam> results =
(
from id in teamIds
let inCurrent = currentLookup[id].Any()
let inUpdated = updatedLookup[id].Any()
let key = inCurrent && inUpdated ? "No Change" :
inCurrent ? "Remove" :
inUpdated ? "Add" :
"Inconceivable"
let teams = key == "Remove" ? currentLookup[id] :
updatedLookup[id]
from team in teams
select new {Key = key, Team = team)
).ToLookup(x => x.Key, x => x.Team);
foreach(UserTeam ut in results["Remove"])
{
RemoveTeamFromDB();
}
foreach(UserTeam ut in results["Add"])
{
AddTeamToDB();
}

LinQ distinct with custom comparer leaves duplicates

I've got the following classes:
public class SupplierCategory : IEquatable<SupplierCategory>
{
public string Name { get; set; }
public string Parent { get; set; }
#region IEquatable<SupplierCategory> Members
public bool Equals(SupplierCategory other)
{
return this.Name == other.Name && this.Parent == other.Parent;
}
#endregion
}
public class CategoryPathComparer : IEqualityComparer<List<SupplierCategory>>
{
#region IEqualityComparer<List<SupplierCategory>> Members
public bool Equals(List<SupplierCategory> x, List<SupplierCategory> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(List<SupplierCategory> obj)
{
return obj.GetHashCode();
}
#endregion
}
And i'm using the following linq query:
CategoryPathComparer comparer = new CategoryPathComparer();
List<List<SupplierCategory>> categoryPaths = (from i in infoList
select
new List<SupplierCategory>() {
new SupplierCategory() { Name = i[3] },
new SupplierCategory() { Name = i[4], Parent = i[3] },
new SupplierCategory() { Name = i[5], Parent = i[4] }}).Distinct(comparer).ToList();
But the distinct does not do what I want it to do, as the following code demonstrates:
comp.Equals(categoryPaths[0], categoryPaths[1]); //returns True
Am I using this in a wrong way? why are they not compared as I intend them to?
Edit:
To demonstrate the the comparer does work, the following returns true as it should:
List<SupplierCategory> list1 = new List<SupplierCategory>() {
new SupplierCategory() { Name = "Cat1" },
new SupplierCategory() { Name = "Cat2", Parent = "Cat1" },
new SupplierCategory() { Name = "Cat3", Parent = "Cat2" }
};
List<SupplierCategory> list1 = new List<SupplierCategory>() {
new SupplierCategory() { Name = "Cat1" },
new SupplierCategory() { Name = "Cat2", Parent = "Cat1" },
new SupplierCategory() { Name = "Cat3", Parent = "Cat2" }
};
CategoryPathComparer comp = new CategoryPathComparer();
Console.WriteLine(comp.Equals(list1, list2).ToString());
Your problem is that you didn't implement IEqualityComparer correctly.
When you implement IEqualityComparer<T>, you must implement GetHashCode so that any two equal objects have the same hashcode.
Otherwise, you will get incorrect behavior, as you're seeing here.
You should implement GetHashCode as follows: (courtesy of this answer)
public int GetHashCode(List<SupplierCategory> obj) {
int hash = 17;
foreach(var value in obj)
hash = hash * 23 + obj.GetHashCode();
return hash;
}
You also need to override GetHashCode in SupplierCategory to be consistent. For example:
public override int GetHashCode() {
int hash = 17;
hash = hash * 23 + Name.GetHashCode();
hash = hash * 23 + Parent.GetHashCode();
return hash;
}
Finally, although you don't need to, you should probably override Equals in SupplierCategory and make it call the Equals method you implemented for IEquatable.
Actually, this issue is even covered in documentation:
http://msdn.microsoft.com/en-us/library/bb338049.aspx.

Resources