How to get intersection of two classes based on property - c#

I have this class
public class FlightSegment{
public string ClassName { get;set;}
}
And I want to get FlightSegments with common ClassName
void Main()
{
var list1 = new List<FlightSegment>() {
new FlightSegment{ ClassName = "A"},
new FlightSegment { ClassName = "B"}
};
var list2 = new List<FlightSegment>() {
new FlightSegment{ ClassName = "B"},
new FlightSegment { ClassName = "C"}
};
var listOfLists = new List<List<FlightSegment>>() { list1, list2 };
var intersection = listOfLists.Aggregate((previousList, nextList) => previousList.Intersect(nextList).ToList());
Console.WriteLine(intersection); //Gives me empty result
}
I tried something like this:
var intersection = listOfLists.Aggregate((previousList, nextList) =>
previousList.Select(e=>e.ClassName).Intersect(nextList.Select(e=>e.ClassName)).ToList());
But gives error:
Cannot convert expression type System.Collection.Generic.List<string> to return type System.Collection.Generic.List<FlightSegment>

You can use Intersect method passing a IEqualityComparer to compare according to ClassName property:
class FlightComparer : EqualityComparer<FlightSegment>
{
public override bool Equals(FlightSegment x, FlightSegment y)
{
return x.ClassName == y.ClassName;
}
public override int GetHashCode(FlightSegment obj)
{
return obj.ClassName.GetHashCode();
}
}
Then, make the intersection using this comparer:
var list1 = new List<FlightSegment>() {
new FlightSegment{ ClassName = "A"},
new FlightSegment { ClassName = "B"}
};
var list2 = new List<FlightSegment>() {
new FlightSegment{ ClassName = "B"},
new FlightSegment { ClassName = "C"}
};
var result = list1.Intersect(list2, new FlightComparer());
This returns a collection containing only one element FlightSegment { ClassName = "B" }.
Using this idea and Aggregate, you can get the intersection of a group of lists based on a property:
var intersection = listOfLists.Aggregate((previousList, nextList) => previousList.Intersect(nextList, new FlightComparer() ).ToList());

Related

how to segregate all double and string data type into a collection of Instance

Here are my class structure and sample data,
public class CollectionProperty
{
public string Name { get; set; }
public object Value { get; set; }
public string DataType { get; set; }
}
public class Instance
{
public string Name { get; set; }
public List<CollectionProperty> CollectionProperties { get; set; }
}
public class CollectionResult
{
public string Asset { get; set; }
public List<Instance> Instances { get; set; }
}
===============================================================================================
var collectionResult = new CollectionResult
{
Asset = "A1",
Instances = new List<Instance>
{
new Instance
{
Name = "Instance-1",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "N1", Value = 10, DataType = "Double"},
new CollectionProperty {Name = "N2", Value = "S1", DataType = "String"}
}
},
new Instance
{
Name = "Instance-2",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "N1", Value = 20, DataType = "Double"},
new CollectionProperty {Name = "N2", Value = "S2", DataType = "String"}
}
}
}
};
Now based on DataType, I want to segregate like below collection. Example is Double, similar for string. In collectionResult, I need to add Asset and all the double data type collection properties from various instances. IS this possible?
var collectionResult = new CollectionResult
{
Asset = "A1",
DoubleInstances = new List<Instance>
{
new Instance
{
Name = "Instance-1",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "N1", Value = 10, DataType = "Double"}
}
},
new Instance
{
Name = "Instance-2",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "N1", Value = 20, DataType = "Double"}
}
}
}
};
In this situation it would be straightforward to create a new CollectionResult instance and do the property filtering with LINQ. You could create new instances with Enumerable.Select()and filter only Doubles with Enumerable.Where():
var doubleCollection = new CollectionResult
{
Asset = collectionResult.Asset,
Instances = collectionResult.Instances.Select(x => new Instance {
Name = x.Name,
CollectionProperties = x.CollectionProperties.Where(cp => cp.DataType == "Double").ToList()
}).ToList()
};
You could also create a method to filter by a Func<CollectionProperty, bool> predicate selector. This approach can be useful if down the track you decide you want to filter by a different property from CollectionProperty. You can just pass in a different predicate.
private static CollectionResult FilterCollectionByProperty(CollectionResult collectionResult, Func<CollectionProperty, bool> selector)
{
return new CollectionResult
{
Asset = collectionResult.Asset,
Instances = collectionResult.Instances.Select(x => new Instance
{
Name = x.Name,
CollectionProperties = x.CollectionProperties.Where(selector).ToList()
}).ToList()
};
}
And create both CollectionResult objects seperately or in IEnumerable<CollectionResult>:
var types = new List<string> { "Double", "String" };
// Separate collections
var doubleCollection = FilterCollectionByProperty(collectionResult, x => x.DataType == "Double");
var stringCollection = FilterCollectionByProperty(collectionResult, x => x.DataType == "String");
// List of collections
var collectionList = types.Select(t => FilterCollectionByProperty(collectionResult, x => x.DataType == t));
The other alternative is to pass in and filter by DataType directly, then you don't need to pass in the Func<CollectionProperty, bool> predicate. Downside is that the method is fixed to filtering by DataType only.
private static CollectionResult FilterCollectionByDataType(CollectionResult collectionResult, string dataType)
{
return new CollectionResult
{
Asset = collectionResult.Asset,
Instances = collectionResult.Instances.Select(x => new Instance
{
Name = x.Name,
CollectionProperties = x.CollectionProperties.Where(cp => cp.DataType == dataType).ToList()
}).ToList()
};
}
Which also can be created into separate CollectionResult objects or a IEnumerable<CollectionResult>:
var types = new List<string> { "Double", "String" };
// Separate collections
var doubleCollection = FilterCollectionByDataType(collectionResult, "Double");
var stringCollection = FilterCollectionByDataType(collectionResult, "String");
// List of collections
var collectionList = types.Select(t => FilterCollectionByDataType(collectionResult, t));

Partition graph by connection degree

I would to ask if there is already algorithms for graphs that partition graphs into subgraphs like the screenshot attached:
Graph has edges
A-B,B-C,C-D, D-E, C-F, F-G
I need to partition it to 3 parts since vertex C has degree of 3:
A-B-C
C-D-E
C-F-G
First I was thinking that I can remove C node and disconnect graph using typical methods. But maybe there already known method to partition graphs by nodes degree?
I wrote a simple algorithm for this. Please note that the graph is needed to be ordered
public static void Main()
{
Console.WriteLine("Hello World");
var str = "A-B,B-C,C-D,D-E,C-F,F-G";
var resultSet = Graph(str.Split(','), '-');
}
public static string[] Graph(string[] s, char delimiter)
{
var resultSet = new List<string>();
var prevOperLeft = "";
var prevOperRight = "";
foreach (var part in s)
{
var oper = part.Split(delimiter);
var left = oper[0];
var right = oper[1];
if (prevOperRight == left)
{
resultSet.Add(string.Format("{0}{1}{2}{3}{4}", prevOperLeft, delimiter, left, delimiter, right));
prevOperLeft = prevOperRight = "";
}
else
{
prevOperLeft = left;
prevOperRight = right;
}
}
return resultSet.ToArray();
}
https://dotnetfiddle.net/e3kmpR
More generic example with LinkedList
public static IList<LinkedList<T>> Graph2<T>(LinkedList<T> ll) where T: class
{
var resultSet = new List<LinkedList<T>>();
T prevOperLeft = null;
T prevOperRight = null;
while (ll.Count > 0)
{
var left = ll.First.Value;
ll.RemoveFirst();
var right = ll.First.Value;
ll.RemoveFirst();
if (prevOperRight != null && prevOperRight.Equals(left))
{
resultSet.Add(new LinkedList<T>(new []{ prevOperLeft, left, right }));
prevOperLeft = prevOperRight = null;
}
else
{
prevOperLeft = left;
prevOperRight = right;
}
}
return resultSet;
}
public static void Main()
{
var A = new MyClass {Name = "A"};
var B = new MyClass {Name = "B"};
var C = new MyClass {Name = "C"};
var D = new MyClass {Name = "D"};
var E = new MyClass {Name = "E"};
var F = new MyClass {Name = "F"};
var G = new MyClass {Name = "G"};
List<MyClass> list = new List<MyClass>
{
A,B,B,C,C,D,D,E,C,F,F,G
};
LinkedList<MyClass> ll = new LinkedList<MyClass>(list);
var resultSet2 = Graph2(ll);
}
class MyClass
{
public string Name { get; set; }
}

LINQ - Compare List objects and List of strings

I have a list of objects (List1) and list of string (List2 - list of Names of the objects)
I need to get all objects from List1 if the object.Name does not exists in List2
How can write this LINQ C#.?
public class Class1
{
public string Name {get;set;}
}
var List1 = new List<Class1>();
var List2 = new List<string>();
var result = List1.Where(x=>!List2.Contains(x.Name)).ToList();
Or:
var result = List1.Where(x=>!List2.Any(n=>n==x.Name)).ToList();
class Program
{
static void Main(string[] args)
{
List<List1Class> listClass = new List<List1Class>();
listClass.Add(new List1Class { ObjectName = "obj1" });
listClass.Add(new List1Class { ObjectName = "obj2" });
listClass.Add(new List1Class { ObjectName = "obj3" });
listClass.Add(new List1Class { ObjectName = "obj4" });
List<string> listString = new List<string>();
listString.Add("obj2");
listString.Add("obj4");
listString.Add("obj5");
var filterlist = listClass.Where(l => !listString.Contains(l.ObjectName)).ToList();
}
}
class List1Class
{
public string ObjectName { get; set; }
//Add other property
}

Unable to generate list based on string array which is inside class

Below is my class and code:
public class Properties
{
public string Prop1;
public string[] Prop2;
}
public IEnumerable<Properties> GetProperties()
{
string [] props = new[] { "abc", "xyz", };
for (int i = 0; i < 4; i++)
{
yield return new Properties { Prop1 = "Prop" + i, Prop2 = props};
}
}
var values = GetProperties().Select(x => new {
prop = x.Prop2.ToString()
}).ToList();
Now I want my values variable to contains list of props but here i am getting props as array but I want it as single string.
Expected Output:
Values[0] : Name : abc
Values[1] : Name : xyz
How can I do this with linq?
Try this:
public class Properties
{
public string Prop1;
public string[] Prop2;
}
public List<Properties> GetProperties()
{
var list = new List<Properties>();
var props = new[] { "abc", "xyz", }.toList();
props.ForEach(p=>{
var np = new Properties { Prop1 = "Prop" + i, Prop2 = props};
list.Add(np);
});
return list;
}
var values = GetProperties().Select(x => new {
prop = x.Prop2.ToString()
}).ToList();
If you want to have this output you wrote for all 4 values, you should use SelectMany:
var values = GetProperties().SelectMany(x => x.Prop2).ToList();
if you want to have exact output you wrote, you should take only the first value of GetProperties() and you don't need any Select:
var values = GetProperties().First().Prop2;

How can I group by a list of elements?

I run into this issue again and again: how can I group a list of objects by a containing list of other objects?
I have a list of objects of type A and each of these objects has an property (lets call it ListProp) which is a list also. ListProp has elements of the type B. There are multiple elements of type A with identically B-objects in ListProp, but the ListProp property reference differs from element to element. How can I group these A-objects the fastest way, where the B-objects in ListProp are identically?
Sample code:
class Program
{
static void Main(string[] args)
{
var exampleList = new List<A>
{
// Should be in first group
new A { ListProp = new List<B>
{
new B { Prop = new C { Number = 0 }},
new B { Prop = new C { Number = 1 }}
}},
// Should be in first group
new A { ListProp = new List<B>
{
new B { Prop = new C { Number = 0 }},
new B { Prop = new C { Number = 1 }}
}},
// Should be in second group
new A { ListProp = new List<B>
{
new B { Prop = new C { Number = 0 }},
new B { Prop = new C { Number = 1 }},
new B { Prop = new C { Number = 1 }}
}},
// Should be in third group
new A { ListProp = new List<B>
{
new B { Prop = new C { Number = 0 }},
new B { Prop = new C { Number = 0 }}
}}
};
// Doesn't work because the reference of ListProp is always different
var groupedExampleList = exampleList.GroupBy(x => x.ListProp);
}
}
class C
{
public int Number { get; set; }
public override bool Equals(object o)
{
if (o is C)
return Number.Equals(((C)o).Number);
else
return false;
}
}
class B
{
public C Prop { get; set; }
}
class A
{
public IList<B> ListProp { get; set; }
}
You can implement IEqualityComparer<List<B>> and use it in the other GroupBy overload.
public class ListOfBEqualityComparer : IEqualityComparer<List<B>>
{
public bool Equals(List<B> x, List<B> y)
{
// you can also implement IEqualityComparer<B> and use the overload
return x.SequenceEqual(y);
}
public int GetHashCode(List<B> obj)
{
//implementation of List<T> may not work for your situation
return obj.GetHashCode();
}
}
Then you can use the overload
var groupedExampleList = exampleList.GroupBy(x => x.ListProp,
new ListOfBEqualityComparer());
Try this:
GroupBy(x => String.Join(",", x.ListProp));
It will group by 0,1; 0,1; 0,1; 0,1,1; 0,1 accordingly.
I would approach this the following way:
Associate each child element (in ListProp property) with its parent
Group the parents by children
Project the results
var data = exampleList.SelectMany(a=>a.ListProp.Select(x=>new{Key = x.Prop.Number, Value = a}))
.GroupBy(x=>x.Key)
.Select(g=>new {Number = g.Key, Items = g.ToList()});

Categories