Insertion sort in one method using class object - c#

I am building my lab for college and I am new to .net framwork. I wanna to sort the data but I need to follow certain rule for this lab one of them is I can not use list it has to be array.
Here is my menu
1 - Sort by Employee Name (ascending)
2 - Sort by Employee Number (ascending)
3 - Sort by Employee Pay Rate (descending)
4 - Sort by Employee Hours (descending)
5 - Sort by Employee Gross Pay (descending)
6 - Exit
I have a data in model class and I store data in my employees array. I shorted my menu 2 but I need global function for all menu.
Sorting code
for (int i = 1; i < employees.Length; i++)
{
Employee current = employees[i];
int j = i - 1;
for (; j >= 0 && current.GetNumber() > employees[j].GetNumber(); j--)
{
employees[j + 1] = employees[j];
}
employees[j + 1] = current;
}
foreach (Employee employee in employees)
{
Console.WriteLine(employee);
}
Question : Is there any way that I can make one function that gives me
different output for each menu because It is criteria that i can use
one function to complete the all menu.
If need anything from my side please comment.

You can use delegates to solve your problem. So you will have a common method to sort your array that will also accept the delegate on how to compare 2 Employees:
public Employee[] Sort(Employee[] employees, Func<Employee, Employee, bool> comparer)
{
for (int i = 1; i < employees.Length; i++)
{
Employee current = employees[i];
int j = i - 1;
for (; j >= 0 && comparer(current,employees[j]); j--)
{
employees[j + 1] = employees[j];
}
employees[j + 1] = current;
}
return employees;
}
And an example of usage will be
Sort(employees, (e1, e2) => e1.GetNumber() > e2.GetNumber())
or
Sort(employees, (e1, e2) => string.Compare(e1.Name, e2.Name) < 0)

I changed your code. One can retrieve property and then use it:
public void Sort(Employee[] employees, string propertyName)
{
var desiredProperty = typeof(Employee).GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
PropertyInfo info = typeof(Employee).GetProperty(propertyName);
if (info == null) { return; }
for (int i = 1; i < employees.Length; i++)
{
Employee current = employees[i];
int j = i - 1;
int curValue = Convert.ToInt32(info.GetValue(current));
int prevValue = Convert.ToInt32(info.GetValue(employees[j]));
for (; j >= 0 && curValue > prevValue; j--)
{
employees[j + 1] = employees[j];
}
employees[j + 1] = current;
}
foreach (Employee employee in employees)
{
Console.WriteLine(info.GetValue(employee));
}
}

create extention class
public static class OrderedEnumerable
{
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer = null)
{
if (source == null)
{
throw new ArgumentNullException();
}
if (keySelector == null)
{
throw new ArgumentNullException();
}
if (comparer == null)
{
comparer = Comparer<TKey>.Default;
}
Comparison<TSource> comparer2 = (x, y) => comparer.Compare(keySelector(x), keySelector(y));
return new OrderedEnumerableImpl<TSource>(source, comparer2);
}
public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer = null)
{
if (source == null)
{
throw new ArgumentNullException();
}
if (keySelector == null)
{
throw new ArgumentNullException();
}
if (comparer == null)
{
comparer = Comparer<TKey>.Default;
}
Comparison<TSource> comparer2 = (x, y) => comparer.Compare(keySelector(y), keySelector(x));
return new OrderedEnumerableImpl<TSource>(source, comparer2);
}
private class OrderedEnumerableImpl<TSource> : IOrderedEnumerable<TSource>
{
private readonly IEnumerable<TSource> Source;
private readonly Comparison<TSource> Comparer;
public OrderedEnumerableImpl(IEnumerable<TSource> source, Comparison<TSource> comparer)
{
Source = source;
Comparer = comparer;
}
public IOrderedEnumerable<TSource> CreateOrderedEnumerable<TKey>(Func<TSource, TKey> keySelector, IComparer<TKey> comparer, bool descending)
{
if (comparer == null)
{
comparer = Comparer<TKey>.Default;
}
Comparison<TSource> comparer2;
if (descending)
{
comparer2 = (x, y) =>
{
int result = Comparer(x, y);
if (result == 0)
{
result = comparer.Compare(keySelector(y), keySelector(x));
}
return result;
};
}
else
{
comparer2 = (x, y) =>
{
int result = Comparer(x, y);
if (result == 0)
{
result = comparer.Compare(keySelector(x), keySelector(y));
}
return result;
};
}
return new OrderedEnumerableImpl<TSource>(Source, comparer2);
}
public IEnumerator<TSource> GetEnumerator()
{
var source = Source.ToArray();
// ** Here you do the sorting! **
Array.Sort(source, Comparer);
for (int i = 0; i < source.Length; i++)
{
yield return source[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
and Use like as follow
public class Employee
{
public int Number { get; set; }
public int GrossPay { get; set; }
public int Hours { get; set; }
public int PayRate { get; set; }
public string Name { get; set; }
}
void Main()
{
var Employee = new Employee[] { new Employee { Name = "acb", Number = 123 }, new Employee { Name = "nmo", Number = 456 }, new Employee { Name = "xyz", Number = 789 } };
var sortedEmpByNum = Employee.OrderBy(x => x.Number);// by number asc
var sortedEmpByNubDesc = Employee.OrderByDescending(x => x.Number); //by number desc
var sortedEmpByName = Employee.OrderBy(x => x.Name); //by name asc
//you shoud use this one
var finalResult = Employee.OrderBy(x => x.Name)
.ThenBy(x => x.Number)
.ThenByDescending(x => x.PayRate)
.ThenByDescending(x => x.Hours)
.ThenByDescending(x => x.GrossPay);
}
credit=> custom-lambda-sort-extension

Related

C# Unify Duplicated Methods different parameter types

I Had to duplicate this method (made by another person) but I wanna know if there is a way to unify it,
this one receives an IList<ViewModelA>,
I need to pass to it an IList<ViewModelB>
with different properties, but the ones used in the method are in both are the same like fatherId or Items
I wanna know if there is a way to unify those methods with inheritance or interface or something else.
private IList<ViewModelA> TrasnformToTree(IEnumerable<ViewModelA> source)
{
var modelGroups = source.GroupBy(i => i.fatherId);
var roots = modelGroups.FirstOrDefault(g => g.Key.HasValue == false).ToList();
if (roots.Count > 0)
{
var dict = modelGroups.Where(g => g.Key.HasValue)
.ToDictionary(g => g.Key.Value, g => g.ToList());
for (int i = 0; i < roots.Count; i++)
{
if (dict.ContainsKey(roots[i].Id))
{
roots[i].Items = dict[roots[i].Id];
for (int j = 0; j < roots[i].Items.Count; j++)
AddChildren(roots[j].Items[j], dict);
}
else
{
roots[i].Items = new List<ViewModelA>();
}
}
}
return roots;
}
this should work for the code i'm seeing:
private interface ISomeInterface<T>
{
int Id { get; set; }
int? fatherId { get; set; }
List<T> Items { get; set; }
}
private IList<T> TrasnformToTree<T>(IEnumerable<T> source) where T : ISomeInterface<T>
{
var modelGroups = source.GroupBy(i => i.fatherId);
var roots = modelGroups.FirstOrDefault(g => g.Key.HasValue == false).ToList();
if (roots.Count > 0)
{
var dict = modelGroups.Where(g => g.Key.HasValue)
.ToDictionary(g => g.Key.Value, g => g.ToList());
for (int i = 0; i < roots.Count; i++)
{
if (dict.ContainsKey(roots[i].Id))
{
roots[i].Items = dict[roots[i].Id];
for (int j = 0; j < roots[i].Items.Count; j++)
AddChildren(roots[j].Items[j], dict);
}
else
{
roots[i].Items = new List<T>();
}
}
}
return roots;
}

What is the best way to group groupings of groupings?

So recently I ran into a problem, my team and I need to take a list of objects, and group them by conditions, then that group by more conditions, then that group by even more conditions, and so on for 7 or so levels. After thinking on it for a few days I finally came up with sort of a tree structure, although each level is manually defined (mainly for ease of reading, because once it is programed it will be set in stone). What is the best method to handle for this, and if possible, why?
Here’s what I have so far using a list of random integers. The checks are: divisible by 2, divisible by 3, and divisible by 5 in that order (although the order of conditions don’t matter for the requirements):
Here's the code for the random list of integers plus the TopNode class
Random rand = new Random();
List<int> ints = new List<int>();
for (int i = 0; i < 10; i++)
{
ints.Add(rand.Next(0, 10000001));
}
TopNode node = new TopNode(ints);
Here's the rest of the code for the top node class
public class TopNode
{
public Even Even { get; set; }
public Odd Odd { get; set; }
public TopNode(List<int> ints)
{
var even = ints.Where(x => x % 2 == 0).ToList();
var odd = ints.Where(x => x % 2 != 0).ToList();
if (even.Count > 0)
{
Even = new Even(even);
}
if (odd.Count > 0)
{
Odd = new Odd(odd);
}
}
}
public class Even {
public Mulitple3 Mulitple3 { get; set; }
public NotMulitple3 NotMulitple3 { get; set; }
public Even(List<int> ints)
{
var multiple = ints.Where(x => x % 3 == 0).ToList();
var not = ints.Where(x => x % 3 != 0).ToList();
if (multiple.Count > 0)
{
Mulitple3 = new Mulitple3(multiple);
}
if (not.Count > 0)
{
NotMulitple3 = new NotMulitple3(not);
}
}
}
public class Odd {
public Mulitple3 Mulitple3 { get; set; }
public NotMulitple3 NotMulitple3 { get; set; }
public Odd(List<int> ints)
{
var multiple = ints.Where(x => x % 3 == 0).ToList();
var not = ints.Where(x => x % 3 != 0).ToList();
if (multiple.Count > 0)
{
Mulitple3 = new Mulitple3(multiple);
}
if (not.Count > 0)
{
NotMulitple3 = new NotMulitple3(not);
}
}
}
public class Mulitple3
{
public Multiple5 Multiple5 { get; set; }
public NotMultiple5 NotMultiple5 { get; set; }
public Mulitple3(List<int> ints)
{
var multiple = ints.Where(x => x % 5 == 0).ToList();
var not = ints.Where(x => x % 5 != 0).ToList();
if (multiple.Count > 0)
{
Multiple5 = new Multiple5(multiple);
}
if (not.Count > 0)
{
NotMultiple5 = new NotMultiple5(not);
}
}
}
public class NotMulitple3
{
public Multiple5 Multiple5 { get; set; }
public NotMultiple5 NotMultiple5 { get; set; }
public NotMulitple3(List<int> ints)
{
var multiple = ints.Where(x => x % 5 == 0).ToList();
var not = ints.Where(x => x % 5 != 0).ToList();
if (multiple.Count > 0)
{
Multiple5 = new Multiple5(multiple);
}
if (not.Count > 0)
{
NotMultiple5 = new NotMultiple5(not);
}
}
}
public class Multiple5
{
public List<int> ints { get; set; }
public Multiple5(List<int> ints)
{
this.ints = ints;
}
}
public class NotMultiple5
{
public List<int> ints { get; set; }
public NotMultiple5(List<int> ints)
{
this.ints = ints;
}
}
The simplest tree is just an IEnumerable<IEnumerable<...>> and you can form it using GroupBy.
Here's a simple example that groups some integers into a tree based on divisibility by 2, 3 and 5. It prints:
{{{{1,7,23,29},{5}},{{3,9,87,21}}},{{{4,8,34,56}},{{78},{30}}}}
.
public static void Main()
{
int[] input = new int[]{1, 3, 4, 5, 7, 8, 9, 23, 34, 56, 78, 87, 29, 21, 2*3*5};
// TREE
var groupedTree = input.GroupBy(x => x % 2 == 0)
.Select(g => g.GroupBy(x => x % 3 == 0)
.Select(h => h.GroupBy(x => x % 5 == 0)));
Console.WriteLine(Display(groupedTree));
}
// Hack code to dump the tree
public static string DisplaySequence(IEnumerable items) => "{" + string.Join(",", items.Cast<object>().Select(x => Display(x))) + "}";
public static string Display(object item) => item is IEnumerable seq ? DisplaySequence(seq) : item.ToString();
I also created a tree class, but I used a class to hold each condition, and an array of conditions to handle the grouping. Each condition is expected to return an int to create the grouping. Then the tree class can step through the conditions to group each level. To make the tree uniform, I kept a list of members at each level which is then split into the next level.
public class Condition<T> {
public string[] Values;
public Func<T, int> Test;
public Condition(string[] values, Func<T, int> test) {
Values = values;
Test = test;
}
}
public class Level {
public static Level<T> MakeTree<T>(IEnumerable<T> src, Condition<T>[] conditions) => new Level<T>(src, conditions);
public static IEnumerable<int> MakeKey<T>(Condition<T>[] conditions, params string[] values) {
for (int depth = 0; depth < values.Length; ++depth)
yield return conditions[depth].Values.IndexOf(values[depth]);
}
}
public class Level<T> {
public string Value;
public Level<T>[] NextLevels;
public List<T> Members;
public Level(string value, List<T> members) {
Value = value;
Members = members;
NextLevels = null;
}
public Level(IEnumerable<T> src, Condition<T>[] conditions) : this("ALL", src.ToList()) => GroupOneLevel(this, 0, conditions);
public void GroupOneLevel(Level<T> parent, int depth, Condition<T>[] conditions) {
var condition = conditions[depth];
var nextLevels = new Level<T>[condition.Values.Length];
for (int j2 = 0; j2 < condition.Values.Length; ++j2) {
nextLevels[j2] = new Level<T>(condition.Values[j2], new List<T>());
}
for (int j2 = 0; j2 < parent.Members.Count; ++j2) {
var member = parent.Members[j2];
nextLevels[condition.Test(member)].Members.Add(member);
}
parent.NextLevels = nextLevels;
if (depth + 1 < conditions.Length)
for (int j3 = 0; j3 < condition.Values.Length; ++j3)
GroupOneLevel(nextLevels[j3], depth + 1, conditions);
}
public List<T> MembersForKey(IEnumerable<int> values) {
var curLevel = this;
foreach (var value in values)
curLevel = curLevel.NextLevels[value];
return curLevel.Members;
}
}
For your example, you can use this like:
var conditions = new[] {
new Condition<int>(new[] { "Even", "Odd" }, n => n & 1),
new Condition<int>(new[] { "Div3", "NOTDiv3" }, n => n % 3 == 0 ? 0 : 1),
new Condition<int>(new[] { "Div5", "NOTDiv5" }, n => n % 5 == 0 ? 0 : 1)
};
var ans = Level.MakeTree(ints, conditions);
And you can lookup a particular part of the tree with:
var evenDiv3 = ans.MembersForKey(Level.MakeKey(conditions, "Even", "Div3"));
My suggestion is to create a collection class that can filter your objects, and returns instances of itself so that the filtering can continue deeper. For example lets assume that your objects are of type MyObject:
class MyObject
{
public int Number { get; }
public MyObject(int number) => this.Number = number;
public override string ToString() => this.Number.ToString();
}
Here is an example of the filtering collection MyCollection, that supports filtering for Odd, Even, Multiple3 and NonMultiple3. The lookups required are created lazily, to avoid allocating memory for searches that will never be requested:
class MyCollection : IEnumerable<MyObject>
{
private readonly IEnumerable<MyObject> _source;
private readonly Lazy<ILookup<bool, MyObject>> _multiple2Lookup;
private readonly Lazy<MyCollection> _even;
private readonly Lazy<MyCollection> _odd;
private readonly Lazy<ILookup<bool, MyObject>> _multiple3Lookup;
private readonly Lazy<MyCollection> _multiple3;
private readonly Lazy<MyCollection> _nonMultiple3;
public MyCollection Even => _even.Value;
public MyCollection Odd => _odd.Value;
public MyCollection Multiple3 => _multiple3.Value;
public MyCollection NonMultiple3 => _nonMultiple3.Value;
public MyCollection(IEnumerable<MyObject> source)
{
_source = source;
_multiple2Lookup = new Lazy<ILookup<bool, MyObject>>(
() => _source.ToLookup(o => o.Number % 2 == 0));
_even = new Lazy<MyCollection>(
() => new MyCollection(_multiple2Lookup.Value[true]));
_odd = new Lazy<MyCollection>(
() => new MyCollection(_multiple2Lookup.Value[false]));
_multiple3Lookup = new Lazy<ILookup<bool, MyObject>>(
() => _source.ToLookup(o => o.Number % 3 == 0));
_multiple3 = new Lazy<MyCollection>(
() => new MyCollection(_multiple3Lookup.Value[true]));
_nonMultiple3 = new Lazy<MyCollection>(
() => new MyCollection(_multiple3Lookup.Value[false]));
}
public IEnumerator<MyObject> GetEnumerator() => _source.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Usage example:
var source = Enumerable.Range(1, 20).Select(i => new MyObject(i));
var myObjects = new MyCollection(source);
var filtered = myObjects.Even.NonMultiple3;
Console.WriteLine(String.Join(", ", filtered));
Output:
2, 4, 8, 10, 14, 16, 20
A possible drawback of this approach is that it allows calling both myObjects.Even.NonMultiple3 and myObjects.NonMultiple3.Even. Both queries return the same results, but cause the creation of redundant lookups.
NetMage and Theodor's answers were exactly what I was looking for as per the question. However, due to an oversite in my example code, I neglected to mention that sometimes the answer would return with more than just true or false and instead would return 3 or 4 values (and in very rare occasions one of the return values needs to be grouped and iterated over). This is not a fault of their own, and their work is actually very good and serves good use, but it was an oversite on my part. Due to this of this I decided to go with Ian's and Kyles answers based on the comments and came up with this:
While it's not perfect, it does allow me to return as many values as I want, group by if I need to (defined in the case statements), and if I only need to filter by 2 and not all 3 or need to change the order, I can add them to the conditions array as I need them.
Again thanks for the help and I'm sorry I wasn't clear enough in the question.
Random rand = new Random();
List<int> ints = new List<int>();
for (int i = 0; i < 10000000; i++)
{
ints.Add(rand.Next(0, 10000001));
}
string[] conditions = new string[] { "even", "div3", "div5" };
var dynamicSort = new Sorted(ints);
public class Sorted
{
public List<List<int>> returnVal { get; set; }
public static List<int> Odd(List<int> ints)
{
return ints.Where(x => x % 2 != 0).ToList();
}
public static List<int> Even(List<int> ints)
{
return ints.Where(x => x % 2 == 0).ToList();
}
public static List<int> DivThree(List<int> ints)
{
return ints.Where(x => x % 3 == 0).ToList();
}
public static List<int> NotDivThree(List<int> ints)
{
return ints.Where(x => x % 3 != 0).ToList();
}
public static List<int> DivFive(List<int> ints)
{
return ints.Where(x => x % 5 == 0).ToList();
}
public static List<int> NotDivFive(List<int> ints)
{
return ints.Where(x => x % 5 != 0).ToList();
}
public Sorted(List<int> ints, string[] conditions)
{
returnVal = GetSorted(ints, conditions, 0);
}
public List<List<int>> GetSorted(List<int>ints, string[] conditions, int index)
{
var sortReturn = new List<List<int>>();
switch (conditions[index].ToLower())
{
case "even":
case "odd":
{
if (index == conditions.Length - 1)
{
sortReturn.Add(Odd(ints));
sortReturn.Add(Even(ints));
}
else
{
var i = ++index;
sortReturn.AddRange(GetSorted(Odd(ints), conditions, i));
sortReturn.AddRange(GetSorted(Even(ints), conditions, i));
}
break;
}
case "div3":
case "notdiv3":
{
if (index == conditions.Length - 1)
{
sortReturn.Add(DivThree(ints));
sortReturn.Add(NotDivThree(ints));
}
else
{
var i = ++index;
sortReturn.AddRange(GetSorted(DivThree(ints), conditions, i));
sortReturn.AddRange(GetSorted(NotDivThree(ints), conditions, i));
}
break;
}
case "div5":
case "notdiv5":
{
if (index == conditions.Length - 1)
{
sortReturn.Add(DivFive(ints));
sortReturn.Add(NotDivFive(ints));
}
else
{
var i = ++index;
sortReturn.AddRange(GetSorted(DivFive(ints), conditions, i));
sortReturn.AddRange(GetSorted(NotDivFive(ints), conditions, i));
}
break;
}
}
return sortReturn;
}
}

How to filter this specific list?

I have the following interfaces.
public interface IProductPrices
{
int ProductId { get; set; }
string ProductCode { get; set; }
List<IProductPrice> Prices { get; set; }
}
public interface IProductPrice
{
int ProductId { get; set; }
string PriceKey { get; }
double Price { get; set; }
int Id { get; set; }
}
Snippet
//productPrices is of type IProductPrices
//temp is of type IProductPrice
.
........
var finalPriceList = new List<ABC.Model.ProductPrice>();
foreach (var item in productPrices)
{
foreach (var temp in item.Prices)
{
var prodPrice = new ABC.Model.ProductPrice()
{
Price = temp.Price,
ProductCode = temp.ProductCode
};
finalPriceList.Add(prodPrice);
}
}
.....
In the snippet above there is the line
foreach (var temp in item.Prices)
I would like to select from [item.Prices] only if the [PriceKey] is equal to "ABC". If [PriceKey]=="ABC" does not exist then I would like to select the item with the lowest [Price].
How to achieve this ?
Using an extension method I wrote for early exiting Aggregate, you can write a special MinBy variation for this problem.
Here is the AggregateUntil variation I used. There are also While variations and variations that take a seed.
public static T AggregateUntil<T>(this IEnumerable<T> src, Func<T, T, T> accumFn, Predicate<T> endFn) {
using (var e = src.GetEnumerator()) {
T ans = default;
if (e.MoveNext()) {
ans = e.Current;
while (!endFn(ans) && e.MoveNext())
ans = accumFn(ans, e.Current);
}
return ans;
}
}
Now you can create a MinBy variation with an early out:
public static T MinByUnless<T, TKey>(this IEnumerable<T> src, Func<T, TKey> minFn, Func<T, bool> unlessFn ) =>
src.AggregateUntil((a, b) => (Comparer<TKey>.Default.Compare(minFn(a), minFn(b)) < 0 && !unlessFn(b)) ? a : b, a => unlessFn(a));
And using it is pretty easy:
if (item.Prices.Any()) {
var temp = item.Prices.MinByUnless(p => p.Price, p => p.PriceKey == "ABC");
From your item.Prices you could check the priceKey == "ABC" else go get the min value with the extension Min like the example.
var finalPriceList = new List<ProductPrice>();
var productPrices = new List<ProductPrice>();
foreach (var item in productPrices)
{
foreach (var temp in item.Prices)
{
if (temp.PriceKey == "ABC")
{
var prodPrice = new ProductPrice()
{
Price = temp.Price,
ProductCode = temp.ProductCode
};
finalPriceList.Add(prodPrice);
}
else
{
int min = item.Prices.Min(entry => entry.Price);
var lowestPrice = item.Prices.Where(w => w.Price == min).Single();
var prodPrice = new ProductPrice()
{
Price = lowestPrice.Price,
ProductCode = lowestPrice.ProductCode
};
finalPriceList.Add(prodPrice);
}
}
}

add dynamic anonymous object into linq groupBy expression

I have following linq expression:
Func<Entity, object> groupQuery = item =>
new { a = item.Attributes["name"], item = item.Attributes["number"] };
var result = target.Collection.Entities.GroupBy(groupQuery).ToList();
But if i don't know, how much columns i will group (for example 3 instead of 2),and names of the Attributes stored in List Names, How should i change my groupQuery object? My first idea was to create dynamic object like this but it don't work
dynamic groupQuery= new ExpandoObject();
IDictionary<string, object> dictionary = (IDictionary<string, object>)groupQuery;
foreach (string str in Names)
{
dictionary.Add(str, str);
}
Instead of returning an object from groupQuery you can return a string. This string will be constructed from properties of objects that you want to group. Depending on the configuration it can be generated in different ways i.e. based on different properties. Here is a code that shows an idea:
public class A
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
}
public enum GroupByuMode
{
GroupBy1,
GroupBy2,
GroupBy3,
}
...
var list = new List<A>();
for (int i = 0; i < 10; ++i)
for (int j = 0; j < 10; ++j)
for (int k = 0; k < 10; ++k)
list.Add(new A { Property1 = i.ToString(), Property2 = j.ToString(), Property3 = k.ToString() });
var mode = GroupByuMode.GroupBy1;
Func<A, object> func = a =>
{
if (mode == GroupByuMode.GroupBy1)
return a.Property1;
else if (mode == GroupByuMode.GroupBy2)
return String.Format("{0}_{1}", a.Property1, a.Property2);
else if (mode == GroupByuMode.GroupBy3)
return String.Format("{0}_{1}_{2}", a.Property1, a.Property2, a.Property3);
return null;
};
var res = list.GroupBy(func).ToList();
Console.WriteLine(res.Count);
mode = GroupByuMode.GroupBy2;
res = list.GroupBy(func).ToList();
Console.WriteLine(res.Count);
It works witch LINQ to Objects as shown above. You have to check if it works with LINQ to Entities or another implementation of LINQ.
answer in question
C# LINQ - How to build Group By clause dynamically
IEnumerable<string> columnsToGroupBy = new[] { Names.First()};
Names.RemoveAt(0);
Names.Aggregate(columnsToGroupBy, (current, query) => current.Concat(new[] {query}));
GroupQuery = r => new NTuple<object>(from column in columnsToGroupBy select r[column]);
///////
using System;
using System.Collections.Generic;
using System.Linq;
namespace WBTCB.AggregationService.Models.Helpers
{
public class NTuple<T> : IEquatable<NTuple<T>>
{
public NTuple(IEnumerable<T> values)
{
Values = values.ToArray();
}
public readonly T[] Values;
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
return true;
if (obj == null)
return false;
return Equals(obj as NTuple<T>);
}
public bool Equals(NTuple<T> other)
{
if (ReferenceEquals(this, other))
return true;
if (other == null)
return false;
var length = Values.Length;
if (length != other.Values.Length)
return false;
for (var i = 0; i < length; ++i)
if (!Equals(Values[i], other.Values[i]))
return false;
return true;
}
public override int GetHashCode()
{
return Values.Aggregate(17, (current, value) => current*37 + (!ReferenceEquals(value, null) ? value.GetHashCode() : 0));
}
}
}

Tarjan cycle detection help C#

Here is a working C# implementation of tarjan's cycle detection.
The algorithm is found here:
http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
public class TarjanCycleDetect
{
private static List<List<Vertex>> StronglyConnectedComponents;
private static Stack<Vertex> S;
private static int index;
private static DepGraph dg;
public static List<List<Vertex>> DetectCycle(DepGraph g)
{
StronglyConnectedComponents = new List<List<Vertex>>();
index = 0;
S = new Stack<Vertex>();
dg = g;
foreach (Vertex v in g.vertices)
{
if (v.index < 0)
{
strongconnect(v);
}
}
return StronglyConnectedComponents;
}
private static void strongconnect(Vertex v)
{
v.index = index;
v.lowlink = index;
index++;
S.Push(v);
foreach (Vertex w in v.dependencies)
{
if (w.index < 0)
{
strongconnect(w);
v.lowlink = Math.Min(v.lowlink, w.lowlink);
}
else if (S.Contains(w))
{
v.lowlink = Math.Min(v.lowlink, w.index);
}
}
if (v.lowlink == v.index)
{
List<Vertex> scc = new List<Vertex>();
Vertex w;
do
{
w = S.Pop();
scc.Add(w);
} while (v != w);
StronglyConnectedComponents.Add(scc);
}
}
Note a DepGraph is just a list of Vertex. and Vertex has a list of other Vertex which represent the edges. Also index and lowlink are initialized to -1
EDIT: This is working...I just misinterpreted the results.
The above is actually correct, I did not understand what a strongly connected component was. I was expecting the function to return an empty List of strongly connected components, yet it was returning a list of single nodes.
I believe the above is working. Feel free to use if you need it!
As of 2008 quickgraph has supported this algorithm. See the StronglyConnectedComponentsAlgorithm class for the implementation, or AlgorithmExtensions.StronglyConnectedComponents method for a usage shortcut.
Example:
// Initialize result dictionary
IDictionary<string, int> comps = new Dictionary<string, int>();
// Run the algorithm
graph.StronglyConnectedComponents(out comps);
// Group and filter the dictionary
var cycles = comps
.GroupBy(x => x.Value, x => x.Key)
.Where(x => x.Count() > 1)
.Select(x => x.ToList())
Example presented above in question isn't functional should anyone want to quickly play with it. Also note that it is stack based, which will detonate your stack if you give anything but the most trivial of graphs. Here is a working example with a unit test that models the graph presented on the Tarjan wikipedia page:
public class Vertex
{
public int Id { get;set; }
public int Index { get; set; }
public int Lowlink { get; set; }
public HashSet<Vertex> Dependencies { get; set; }
public Vertex()
{
Id = -1;
Index = -1;
Lowlink = -1;
Dependencies = new HashSet<Vertex>();
}
public override string ToString()
{
return string.Format("Vertex Id {0}", Id);
}
public override int GetHashCode()
{
return Id;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
Vertex other = obj as Vertex;
if (other == null)
return false;
return Id == other.Id;
}
}
public class TarjanCycleDetectStack
{
protected List<List<Vertex>> _StronglyConnectedComponents;
protected Stack<Vertex> _Stack;
protected int _Index;
public List<List<Vertex>> DetectCycle(List<Vertex> graph_nodes)
{
_StronglyConnectedComponents = new List<List<Vertex>>();
_Index = 0;
_Stack = new Stack<Vertex>();
foreach (Vertex v in graph_nodes)
{
if (v.Index < 0)
{
StronglyConnect(v);
}
}
return _StronglyConnectedComponents;
}
private void StronglyConnect(Vertex v)
{
v.Index = _Index;
v.Lowlink = _Index;
_Index++;
_Stack.Push(v);
foreach (Vertex w in v.Dependencies)
{
if (w.Index < 0)
{
StronglyConnect(w);
v.Lowlink = Math.Min(v.Lowlink, w.Lowlink);
}
else if (_Stack.Contains(w))
{
v.Lowlink = Math.Min(v.Lowlink, w.Index);
}
}
if (v.Lowlink == v.Index)
{
List<Vertex> cycle = new List<Vertex>();
Vertex w;
do
{
w = _Stack.Pop();
cycle.Add(w);
} while (v != w);
_StronglyConnectedComponents.Add(cycle);
}
}
}
[TestMethod()]
public void TarjanStackTest()
{
// tests simple model presented on https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
var graph_nodes = new List<Vertex>();
var v1 = new Vertex() { Id = 1 };
var v2 = new Vertex() { Id = 2 };
var v3 = new Vertex() { Id = 3 };
var v4 = new Vertex() { Id = 4 };
var v5 = new Vertex() { Id = 5 };
var v6 = new Vertex() { Id = 6 };
var v7 = new Vertex() { Id = 7 };
var v8 = new Vertex() { Id = 8 };
v1.Dependencies.Add(v2);
v2.Dependencies.Add(v3);
v3.Dependencies.Add(v1);
v4.Dependencies.Add(v3);
v4.Dependencies.Add(v5);
v5.Dependencies.Add(v4);
v5.Dependencies.Add(v6);
v6.Dependencies.Add(v3);
v6.Dependencies.Add(v7);
v7.Dependencies.Add(v6);
v8.Dependencies.Add(v7);
v8.Dependencies.Add(v5);
v8.Dependencies.Add(v8);
graph_nodes.Add(v1);
graph_nodes.Add(v2);
graph_nodes.Add(v3);
graph_nodes.Add(v4);
graph_nodes.Add(v5);
graph_nodes.Add(v6);
graph_nodes.Add(v7);
graph_nodes.Add(v8);
var tcd = new TarjanCycleDetectStack();
var cycle_list = tcd.DetectCycle(graph_nodes);
Assert.IsTrue(cycle_list.Count == 4);
}
I added a Id property to the Vertex object so it is simple to see what is being done, it isn't strictly needed. I also cleaned up some of the code a little, author was using naming from page pseudo-code, which is good for comparison, but it wasn't very informative.

Categories

Resources