Tarjan cycle detection help C# - 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.

Related

Linq Aggregate gives null value rather than expected return List

I'm attempting to utilize linq aggregate in order to mimic a reduce behavior. I need GroupingMethod to return a List of arrays.For some reason GroupingMethod returns null no matter what. I appreciate any help.
List<Group> groupings = GroupingMethod(new int[] {9,6,9,3,5,3,6,6,3,5,5,1,6,9,8});
static List<Group> GroupingMethod(int[] ValueList)
{
int groupTotal = 0;
Group newGroup = new Group();
var put = ValueList.Aggregate(new List<Group>(), (acc, x) =>
{
List<Group> polp = acc;
groupTotal = groupTotal + x;
if (groupTotal < 10)
{
newGroup.AddInt(x);
} else
{
polp.Add(newGroup);
newGroup.Clear();
groupTotal = 0;
}
return polp;
});
return put;
}
class Group
{
List<int> iGrouping = new List<int>();
public Group(int AddInt)
{
iGrouping.Add(AddInt);
}
public Group()
{
}
public void AddInt(int IntToAdd)
{
iGrouping.Add(IntToAdd);
}
public void Clear()
{
iGrouping.Clear();
}
public int CombinedGroupValue { get{return iGrouping.Sum();}}
}

How can i fix this method using Linq so that the test (which should already work because the result is correct) works?

So, here's the problem:
I have a list of products:
public class ProductWithFeatures
{
public string Name { get; set; }
public ICollection<Feature> Features { get; set; }
}
public class Feature
{
public int Id { get; set; }
public Feature(int Id)
{
this.Id = Id;
}
}
I need to filter the list, using Linq functions, so that the only remaining items are the products that have at least one feature from a list of given features.
This is what i wrote:
public class ProductListFilteringClass
{
public List<ProductWithFeatures> products;
public ProductListFilteringClass(List<ProductWithFeatures> list)
{
this.products = list;
}
public List<ProductWithFeatures> ProductsWithAtLeastOneFeatureFromTheList(ICollection<Feature> features)
{
Func<ProductWithFeatures, bool> doesItHaveTheFeatures = x =>
{
FeatureComparer comparer = new FeatureComparer();
bool b = x.Features.Any(y => features.Contains(y, comparer));
return b;
};
return products.Where(doesItHaveTheFeatures).ToList();
}
}
public class FeatureComparer : IEqualityComparer<Feature>
{
public bool Equals(Feature x, Feature y)
{
return x.Id == y.Id;
}
public int GetHashCode(Feature obj)
{
return obj.Id;
}
}
Problem is, when i try to run a test on this, it results the correct answer but it doesn't match. Here's the test:
[Fact]
public void CheckIfReturnProductsWithAtLeastOneFeatureFromTheListWorksCorrectly()
{
LinqHomework.Feature[] ids = new LinqHomework.Feature[6];
ids[0] = new LinqHomework.Feature(1);
ids[1] = new LinqHomework.Feature(3);
ids[2] = new LinqHomework.Feature(5);
ids[3] = new LinqHomework.Feature(7);
ids[4] = new LinqHomework.Feature(9);
ids[5] = new LinqHomework.Feature(11);
List<LinqHomework.ProductWithFeatures> list = new List<LinqHomework.ProductWithFeatures>();
LinqHomework.Feature[] featuresA = new LinqHomework.Feature[3];
featuresA[0] = new LinqHomework.Feature(0);
featuresA[1] = new LinqHomework.Feature(5);
featuresA[2] = new LinqHomework.Feature(2);
LinqHomework.ProductWithFeatures productA = new LinqHomework.ProductWithFeatures();
productA.Name = "a";
productA.Features = featuresA;
list.Add(productA);
LinqHomework.Feature[] featuresB = new LinqHomework.Feature[3];
featuresB[0] = new LinqHomework.Feature(1);
featuresB[1] = new LinqHomework.Feature(3);
featuresB[2] = new LinqHomework.Feature(7);
LinqHomework.ProductWithFeatures productB = new LinqHomework.ProductWithFeatures();
productB.Name = "b";
productB.Features = featuresB;
list.Add(productB);
LinqHomework.Feature[] featuresC = new LinqHomework.Feature[3];
featuresC[0] = new LinqHomework.Feature(10);
featuresC[1] = new LinqHomework.Feature(4);
featuresC[2] = new LinqHomework.Feature(8);
LinqHomework.ProductWithFeatures productC = new LinqHomework.ProductWithFeatures();
productC.Name = "c";
productC.Features = featuresC;
list.Add(productC);
LinqHomework.ProductListFilteringClass productList = new LinqHomework.ProductListFilteringClass(list);
List<LinqHomework.ProductWithFeatures> final = new List<LinqHomework.ProductWithFeatures>();
LinqHomework.Feature[] features1 = new LinqHomework.Feature[3];
features1[0] = new LinqHomework.Feature(0);
features1[1] = new LinqHomework.Feature(5);
features1[2] = new LinqHomework.Feature(2);
LinqHomework.ProductWithFeatures product1 = new LinqHomework.ProductWithFeatures();
product1.Name = "a";
product1.Features = features1;
final.Add(product1);
LinqHomework.Feature[] features2 = new LinqHomework.Feature[3];
features2[0] = new LinqHomework.Feature(1);
features2[1] = new LinqHomework.Feature(3);
features2[2] = new LinqHomework.Feature(7);
LinqHomework.ProductWithFeatures product2 = new LinqHomework.ProductWithFeatures();
product2.Name = "b";
product2.Features = features2;
final.Add(product2);
var x = new ProductComparer();
Assert.Equal(final, productList.ProductsWithAtLeastOneFeatureFromTheList(ids), x);
}
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name && x.Features == y.Features;
}
public int GetHashCode(ProductWithFeatures obj)
{
string toHash = obj.Name;
foreach (var feature in obj.Features)
toHash += feature.GetHashCode();
return toHash.GetHashCode();
}
}
And here's the result:
Result Message:
Assert.Equal() Failure
Expected: List<ProductWithFeatures> [ProductWithFeatures { Features = [...], Name = "a" }, ProductWithFeatures { Features = [...], Name = "b" }]
Actual: List<ProductWithFeatures> [ProductWithFeatures { Features = [...], Name = "a" }, ProductWithFeatures { Features = [...], Name = "b" }]
It's identical, but it doesn't match. How can i fix this?
I would be looking at this part of your equality check:
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name && x.Features == y.Features;
}
The name comparison will work fine, but you are using the == operator to compare two collections, which will give you a reference equality check (is this the same instance of the collection) when it looks like you are interested in the values. You could try x.Features.SequenceEquals(y.Features) if you are confident they will be in the same order. More on list equality here.
Your ProductComparer method is comparing Features without using your special FeatureComparer method.
Try
public class ProductComparer : IEqualityComparer<ProductWithFeatures>
{
FeatureComparer featureComparer = new FeatureComparer();
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name &&
System.Linq.Enumerable.SequenceEqual( x.Features , y.Features, featureComparer);
}
...

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;
}
}

Is it possible to get the item index using Contains?

When I use if (moscowCars.Contains(cars[x].Name)) it founds the value in a collection and I believe that not necessary to use moscowCars.RemoveAt(moscowCars.FindIndex(o => o.Equals(cars[x].Name))); to find it for a second time, just: moscowCars.Remove(cars[x].Name);. Of course, I can use try & catch instead of if, but I just want to know can I get the item index using Contains?
using System.Collections.Generic;
namespace Autoworld
{
class GoodCars
{
static List<Tech> cars = new List<Tech>();
public class Tech
{
public string Name { get; set; }
public double KM { get; set; }
}
static void Main()
{
List<string> moscowCars = new List<string>
{
"GAZ-330811 Aper", "Lada Vesta Sport"
};
cars.Add(new Tech() { Name = "Lada Vesta Sport", KM = 190 });
for (int x = 0; x < cars.Count; x++)
{
if (moscowCars.Contains(cars[x].Name))
{
moscowCars.RemoveAt(moscowCars.FindIndex(o => o.Equals(cars[x].Name)));
}
}
}
}
}
You could remove the two-step process entirely and just use .Remove which will return:
true if item is successfully removed; otherwise, false. This method
also returns false if itemwas not found in the List.
This would then look like:
for (int x = 0; x < cars.Count; x++)
{
moscowCars.Remove(cars[x].Name);
}
And if you need to handle the case where no car is found to be removed, you can wrap that call in an if condition like:
for (int x = 0; x < cars.Count; x++)
{
if (!moscowCars.Remove(cars[x].Name))
{
// Handle no cars to remove
}
}
Worth noting that behind the scenes, .Remove ultimately just gets the index and then removes the item at that index (which is what you were originally trying to do anyways):
public bool Remove(T item) {
int index = IndexOf(item);
if (index >= 0) {
RemoveAt(index);
return true;
}
return false;
}
See here for the source.
Alternatively, as others have stated, if you expect the List to contain more than item to be removed, you can use .RemoveAll:
moscowCars.RemoveAll(y => y == cars[x].Name);
And again, to handle the case where nothing is found:
if (moscowCars.RemoveAll(y => y == cars[x].Name) == 0)
{
// Handle no cars to remove
}
You can indeed use IndexOf(item)
this will give you the index of the item, or -1 if 'item' was not found (making this method double as a "contains" as well)
Use simply RemoveAt if you are sure you don't have any duplicated items anyway use the second way.
Solution
static List<Tech> cars = new List<Tech>();
public class Tech
{
public string Name { get; set; }
public double KM { get; set; }
}
static void Main()
{
List<string> moscowCars = new List<string>
{
"GAZ-330811 Aper", "Lada Vesta Sport"
};
cars.Add(new Tech() { Name = "Lada Vesta Sport", KM = 190 });
for (int x = 0; x < cars.Count; x++)
{
if (moscowCars.Contains(cars[x].Name))
{
moscowCars.RemoveAt(moscowCars.IndexOf(cars[x].Name));
}
}
}
Solution
static List<Tech> cars = new List<Tech>();
public class Tech
{
public string Name { get; set; }
public double KM { get; set; }
}
static void Main()
{
List<string> moscowCars = new List<string>
{
"GAZ-330811 Aper", "Lada Vesta Sport"
};
cars.Add(new Tech() { Name = "Lada Vesta Sport", KM = 190 });
for (int x = 0; x < cars.Count; x++)
{
if (moscowCars.Contains(cars[x].Name))
{
moscowCars.RemoveAll(o => o == cars[x].Name);
}
}
}
I hope it will help.

Remove item from List that I'm iterating, or filtering complex List of duplicates

Ok, maybe I'm just blind, but answer is eluding me:
so, model:
public class Dimensions
{
public int Width { get; set; }
public int Height { get; set; }
public int TypeId { get; set; }
public int PeacesForItem { get; set; }
}
and I have a method that is filtering the List:
public List<Dimensions> Consolidation(List<Dimensions> vm)
{
var list = new List<Dimensions>();
if (vm != null)
{
var typeIds = vm.Select(x => x.TypeId).ToHashSet();
foreach (var t in typeIds)
{
foreach(var item in vm)
{
if (t == item.IvericaId)
{
int x = 0;
foreach (var again in vm)
{
if (item.Duzina == again.Duzina && item.Sirina == again.Sirina && item.TypeId== again.TypeId)
{
x ++;
// vm.Remove(item); Not working, who would figure
}
}
list.Add(
new Dimensions
{
Width = item.Width,
Height = item.Height,
TypeId= item.TypeId,
PeacesForItem = x * item.PeacesForItem,
}
);
}
}
}
}
return list;
}
This method is iterating thru List items and checks if there are elements of same dimensions. If there are, then it is doubling needed quantity.
QUESTION: This code is still adding duplicates to new list, and I need to filter that out.
I tried numerous approaches, but every one I came up with has some fatal flaw in the design.
public List<Dimensions> Consolidation(List<Dimensions> vm)
{
return vm.GroupBy(d=>new {d.TypeId, d.Width, d.Height}) // if there are any duplicates, they are grouped here
.Select(g=>new Dimensions(){TypeId = g.Key.TypeId ,
Width = g.Key.Width,
Height = g.Key.Height,
PeacesForItem = g.Sum(dim=>dim.PeacesForItem)}) // number of duplicates in group calculated
.ToList();
}
You've massively over-complicated this one :). You don't need the nested loop, plus you're not doubling the value, you're squaring it? Try this:
var list = new List<Dimensions>();
if (vm != null)
{
foreach (var item in vm)
{
//Not sure how you identify dupes, may need to change the filter here
var duplicate = vm
.Where(v => v.PeacesForItem == item.PeacesForItem);
if (dupes.Any())
{
list.Add(new Dimension
{
Width = item.Width,
Height = item.Height,
TypeId = item.TypeId,
PeacesForItem = item.PeacesForItem * 2;
});
}
}
}
return list;
dublicate the vm-List and iterate over the duplicated list and delete the object form the original list
public List<Dimensions> Consolidation(List<Dimensions> vm)
{
var list = new List<Dimensions>();
if (vm != null)
{
var dublicate = new List<Dimensions>(vm);
var typeIds = vm.Select(x => x.TypeId).ToHashSet();
foreach (var t in typeIds)
{
foreach(var item in dublicate )
{
if (t == item.IvericaId)
{
int x = 0;
foreach (var again in dublicate )
{
if (item.Duzina == again.Duzina && item.Sirina == again.Sirina && item.TypeId== again.TypeId)
{
x ++;
vm.Remove(item); //Now working
}
}
list.Add(
new Dimensions
{
Width = item.Width,
Height = item.Height,
TypeId= item.TypeId,
PeacesForItem = x * item.PeacesForItem,
}
);
}
}
}
}
return list;
}

Categories

Resources