I have a class with one property of List<String> to hold a dynamic list of one or more string ids.
public class FieldCompareItem
{
public List<string> Fields = new List<string>();
public FieldCompareItem(string[] fields)
{
for (int i = 0; i < fields.Count(); i++)
Fields.Add(fields[i]);
}
}
}
I'm trying to compare 2 lists to see if the string arrays match but it doesn't work. Basically, I want to do an A/B compare to get items that only exist in A, in B, and in both, something like this:
var listA = new List<FieldCompareItem>
{
new FieldCompareItem(new[] {"a1"}),
new FieldCompareItem(new[] {"a2"}),
new FieldCompareItem(new[] {"a3","001"})
};
var listB = new List<FieldCompareItem>
{
new FieldCompareItem(new[] {"a2"}),
new FieldCompareItem(new[] {"a3"}),
new FieldCompareItem(new[] {"a3","001"}),
new FieldCompareItem(new[] {"a4"}),
new FieldCompareItem(new[] {"a5"})
};
//exists in A only
var aOnly = listA.Except(listB).ToList();
//expect a1,a3
//exists in B only
var bOnly = listB.Except(listA).ToList();
//expect a4,a5
//exists in both - this may be used for update A>B or B>A
var inBoth = ?????
//expect a2
Because they are values within an array property it doesnt seem to find by criteria. any help appreciated
Create a comparer first:
public class FieldCompareItemComparer: IEqualityComparer<FieldCompareItem>
{
public bool Equals(FieldCompareItem x, FieldCompareItem y)
{
var result = x.Fields.SequenceEqual(y.Fields);
return result;
}
public int GetHashCode(FieldCompareItem obj)
{
return String.Concat(obj.Fields).GetHashCode();
}
}
then use it like the following:
var comparer = new FieldCompareItemComparer();
// exists in A only
var aOnly = listA.Except(listB, comparer).ToList();
// exists in B only
var bOnly = listB.Except(listA, comparer).ToList();
// exists in both
var inBoth = listA.Intersect(listB, comparer).ToList();
Related
I have 6 lists.
List<double> listdSignal0X = new List<double>();
List<double> listdSignal0Y = new List<double>();
List<double> listdSignal1X = new List<double>();
List<double> listdSignal1Y = new List<double>();
List<double> listdSignal2X = new List<double>();
List<double> listdSignal2Y = new List<double>();
I want to group them in XY pairs in a List<List>.
List<List<double>> listlistdXYSignals = new List<List<double>>();
listlistdXYSignals.Add(listdSignal0X);
listlistdXYSignals.Add(listdSignal0Y);
listlistdXYSignals.Add(listdSignal1X);
listlistdXYSignals.Add(listdSignal1Y);
etc.
This does not group them in pairs. I want
listlistdXYSignals[0] ... to retrieve both ... listdSignal0X ... and ... listdSignal0Y
listlistdXYSignals[1] ... to retrieve both ... listdSignal1X ... and ... listdSignal1Y
etc.
The pairs of XY lists are the same length. Each pair of XY lists could be different lengths. Signal1X and Signal1Y are the same length (1000 values). Signal1X is a different length than Signal2X (200 values).
These pairs of XY lists are passed to a function that creates multiple plots on a WPF xaml.
I have tried:
List<Tuple<List<double>, List<double>>> listtupledSignals = new List<Tuple<List<double>, List<double>>>();
listtupledSignals.Add(listdSignalX, listdSignalY);
Error message, "No overload for method 'Add' takes 2 arguments".
How do I group pairs of
List<double>
in a
List<List<double>>
?
List<List<double>, List<double>>
does not work either.
You could create a class to hold the pair of lists:
class SignalListPair
{
public List<double> X { get; set; } = new List<double>();
public List<double> Y { get; set; } = new List<double>();
}
static void Main(string[] args)
{
var signal0XY = new SignalListPair();
var signal1XY = new SignalListPair();
var signal2XY = new SignalListPair();
var signals = new List<SignalListPair>();
signals.Add(signal0XY);
signals.Add(signal1XY);
signals.Add(signal2XY);
var x0 = signals[0].X;
var y0 = signals[0].Y;
var x1 = signals[1].X;
var y1 = signals[1].Y;
}
You need to ensure that the size of the lists are equal.
public class Point
{
public double X { get; set; }
public double Y { get; set; }
}
var listdSignal0Y = new List<double>(new []
{
1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9
});
var listdSignal0X = new List<double>( new []
{
2.1,2.2,2.3,2.4,2.5,2.6,2.7,2.8,2.9
});
var listdSignal2X = new List<double>(new []
{
3.1,3.2,3.3,3.4,3.5,3.6,3.7,3.8,3.9
});
var listdSignal2Y = new List<double>(new []
{
4.1,4.2,4.3,4.4,4.5,4.6,4.7,4.8,4.9
});
var listdSignal1X = new List<double>(new []
{
5.1,5.2,5.3,5.4,5.5,5.6,5.7,5.8,5.9
});
var listdSignal1Y = new List<double>(new []
{
6.1,6.2,6.3,6.4,6.5,6.6,6.7,6.8,6.9
});
var listdXYSignals = new List<Point>();
if (listdSignal0X.Count == listdSignal0Y.Count)
{
for (var i = 0; i < listdSignal0X.Count; i++)
{
listdXYSignals.Add(
new Point
{
X = listdSignal0X[i],
Y = listdSignal0Y[i]
});
}
}
foreach (var o in listdXYSignals)
{
Console.WriteLine($"X: {o.X} - Y: {o.Y}");
}
Try using tuples; they are a very clean solution to your problem
var listOfXYPairs = new List<(double, double)>();
listOfXYPairs.Add((listdSignal0X, listdSignal0Y)); // remember about the ((x, y)), a tuple (x, y) is a single argument
It works to create:
List<Point> listPointsTop = new List<Point>();
List<Point> listPointsMiddle = new List<Point>();
List<Point> listPointsBottom = new List<Point>();
then
CreateXYPoints(listdSignal0X, listdSignal0Y, ref listPointsTop);
CreateXYPoints(listdSignal1X, listdSignal1Y, ref listPointsMiddle);
CreateXYPoints(listdSignal2X, listdSignal2Y, ref listPointsBottom);
private void CreateXYPoints(List<double> listdSignalX, List<double> listdSignalY, ref List<Point> listPoints)
{
int iNumOfPoints = listdSignalX.Count;
for (int ii = 0; ii < iNumOfPoints; ii++) {
double dX = listdSignalX[ii];
double dY = listdSignalY[ii];
Point pt = new Point(dX, dY);
listPoints.Add(pt);
};
}//CreateXYPoints
Then use:
List<List<Point>> listlistpoxySignals = new List<List<Point>>();
listlistpoxySignals.Add(listPointsTop);
listlistpoxySignals.Add(listPointsMiddle);
listlistpoxySignals.Add(listPointsBottom);
listlistpoxySignals is passed to the plot xaml function.
listlistpoxySignals[0] contains contents of listdSignal0X, listdSignal0Y
listlistpoxySignals[1] contains contents of listdSignal1X, listdSignal1Y
listlistpoxySignals[2] contains contents of listdSignal2X, listdSignal2Y.
You could try zipping them up using LINQ if they are of equal length.
List<List<(double,double)>> listlistdXYSignals = new List<List<(double,double)>>();
listlistdXYSignals.Add(listdSignal0X.Zip(listdSignal0Y).ToList());
listlistdXYSignals.Add(listdSignal1X.Zip(listdSignal1Y).ToList());
etc.
I think what you need is a List<Tuple<List<double>, List<double>>>
Ref.: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples
Say I have a data class like this and a list of its objects:
public class DataSet
{
public int A { get; set; }
public string B { get; set; }
public double C { get; set; }
}
var data = new List<DataSet>
{
new DataSet() { A = 1, B = "One", C = 1.1 },
new DataSet() { A = 2, B = "Two", C = 2.2 },
new DataSet() { A = 3, B = "Three", C = 3.3 }
};
I would like to do a Select() on the list, based on different properties. For example, if I need a list of property A, I could do this easily:
var listA = data.Select(x => x.A).ToList();
All good so far.
But in my program, I need to do the above, only, I wouldn't know whether I need a list of A or B or C until runtime. This 'knowledge' of what to select is stored in a list of strings, and I need to iterate it and extract only the appropriate lists. Something like this:
// GetKeys() will return the keys that I need to extract.
// So at one time keyList could have "A" and "B", another time "B" and "C" etc.
List<string> keyList = GetKeys();
foreach (var key in keyList)
{
// What do I do here?
data.Select(x =>???).ToList();
}
Is this possible at all? I'm fine with even a non-LINQ solution, if it achieves my goal.
EDIT:
Clarifying the requirement.
The end result I want is a separate list based on each 'key' mentioned above. So, something like
List<List<object>>
The count in outer list would be the count of keyList.
The inner list would have as many items as in DataSet.
This would probably not be the most efficient solution, but you could use Reflection for a fully dynamic solution:
private static List<List<object>> SelectDynamicData<T>(IEnumerable<T> data, List<string> properties)
{
// get the properties only once per call
// this isn't fast
var wantedProperties = typeof(T)
.GetProperties()
.Where(x => properties.Contains(x.Name))
.ToArray();
var result = new Dictionary<string, List<object>>();
foreach (var item in data)
{
foreach (var wantedProperty in wantedProperties)
{
if (!result.ContainsKey(wantedProperty.Name))
{
result.Add(wantedProperty.Name, new List<object>());
}
result[wantedProperty.Name].Add(wantedProperty.GetValue(item));
}
}
return result.Select(x => x.Value).ToList();
}
And, of course, you'd need to do a double foreach or a LINQ query to print that. For example:
var data = new List<DataSet>
{
new DataSet() { A = 1, B = "One", C = 1.1 },
new DataSet() { A = 2, B = "Two", C = 2.2 },
new DataSet() { A = 3, B = "Three", C = 3.3 }
};
var selectedData = SelectDynamicData(data, new List<string> { "A", "C" });
foreach (var list in selectedData)
{
foreach (object item in list)
{
Console.Write(item + ", ");
}
Console.WriteLine();
}
Using Creating Expression Trees by Using the API you can build an expression tree to represent the linq query you were hard coding in order to make it more dynamic.
Expression<Func<TModel, object>> GetPropertyExpression<TModel>(string propertyName) {
// Manually build the expression tree for
// the lambda expression v => v.PropertyName.
// (TModel v) =>
var parameter = Expression.Parameter(typeof(TModel), "v");
// (TModel v) => v.PropertyName
var property = Expression.Property(parameter, propertyName);
// (TModel v) => (object) v.PropertyName
var cast = Expression.Convert(property, typeof(object));
var expression = Expression.Lambda<Func<TModel, object>>(cast, parameter);
return expression;
}
Review the comments to understand the building of the expression tree.
This now can be used with the data to extract the desired result.
Following similar to what was provided in another answer it would be simplified to
List<List<object>> SelectDynamicData<T>(IEnumerable<T> data, List<string> properties) {
return properties
.Select(_ => data.Select(GetPropertyExpression<T>(_).Compile()).ToList())
.ToList();
}
Both methods are displayed in the following example
[TestMethod]
public void TestMethod1() {
var data = new List<DataSet>
{
new DataSet() { A = 1, B = "One", C = 1.1 },
new DataSet() { A = 2, B = "Two", C = 2.2 },
new DataSet() { A = 3, B = "Three", C = 3.3 }
};
var propertyKnownAtRuntime = "A";
var expression = GetPropertyExpression<DataSet>(propertyKnownAtRuntime);
var listA = data.Select(expression.Compile()).ToList();
//Produces
// { 1, 2, 3}
var listAC = SelectDynamicData(data, new List<string> { "A", "C" });
//Produces
//{
// { 1, 2, 3},
// { 1.1, 2.2, 3.3 }
//}
}
You can use reflection, for example
string key = "A";
var query = data.Select(x =>
{
var prop = x.GetType().GetProperty(key); //NOTE: if key does not exist this will return null
return prop.GetValue(x);
});
foreach (var value in query)
{
Console.WriteLine(value); //will print 1, 2, 3
}
I want to compare two non related entities by using linq.
e.g -
Entity A
Id Name
1 A
2 B
3 C
4 D
Entity B
Id Name
1 B
2 C
Result I should get
A, D
From the above two collection I want to compare Entity B with Entity A by using the Name property and find out the records which are not available in Entity B.
Any help will be appreciated with some sample code.
Regards
You can use the Except extension method of LINQ. Quote from MSDN Documentation...
Produces the set difference of two sequences by using the default equality comparer to compare values.
Sample Code
int[] a = { 1, 2, 3, 4, 5 };
int[] b = { 4,5,6,7 };
var c = a.Except(b);
Result
1,2,3
Note
Because you are working with a custom object (a class) you will need to implement an equality comparer that compares items by the Name property. Example of custom equality comparer...
public class CustomComparer : IEqualityComparer<CustomObject>
{
public bool Equals(CustomObject x, CustomObject y)
{
return x.Name.Equals(y);
}
public int GetHashCode(CustomObject obj)
{
return obj.Name.GetHashCode();
}
}
Then you can use this custom equality comparer in an overload of the Except extension method, assumming a and b are of type CustomObject...
var c = a.Except(b, new CustomComparer());
The advantage is re-usability, especially if you are spreading this call to Except all over your project. Then,if you need to change your entity(custom object) you only have make changes in the custom equality comparer
var result = entityAs.Where(a => !entityBs.Any(b => b.Name == a.Name)).ToList();
Create a Class
class MyClass
{
public int Id {get; set;}
public string Name {get; set;}
}
Create List1
List<MyCLass> li1 = new List<MyCLass>();
MyCLass o1 = new MyClass();
o1.Id = 1;
o1.Name = "A";
li1.Add(o1);
o1 = new
o1.Id = 2;
o1.Name = "B";
li1.Add(o1);
o1 = new new MyClass();
o1.Id = 3;
o1.Name = "C";
li1.Add(o1);
o1 = new new MyClass();
o1.Id = 4;
o1.Name = "D";
li1.Add(o1);
Create List2
List<MyCLass> li2 = new List<MyCLass>();
o1 = new new MyClass();
o1.Id = 1;
o1.Name = "B";
li2.Add(o1);
o1 = new new MyClass();
o1.Id = 2;
o1.Name = "C";
li2.Add(o1);
o1 = new new MyClass();
o1.Id = 3;
o1.Name = "D";
li2.Add(o1);
Select only Selected items which you want to compare from List1
List<string> SelectedName = li1.Select(s => s.Name).ToList();
FinalList1 only Get those Item which are in List2
var FinalList = li2.Where(w => SelectedName.Contains(w.Name)).ToList();
/// or
FinalList2 only Get those Item which are not in List2
var FinalList2 = li2.Where(w => !SelectedName.Contains(w.Name)).ToList();
I have been trying to figure out how to randomly order two lists the same eg.
List<string> list = new List<string>();
list.Add("RedHat");
list.Add("BlueHat");
list.Add("YellowHat");
List<image> list2 = new List<image>();
list.Add(Properties.Resources.RedHat);
list.Add(Properties.Resources.BlueHat);
list.Add(Properties.Resources.YellowHat);
now if i wanted to order these so that redhat and the redhat image stay aligned how may i do this?And is there a way to combine these lists and then shuffle using a dictionary or keyvalue pair or something along those lines?
Wrap the two in an object:
class WrapperObject {
public string Name { get; set; }
public object Resource { get; set; }
}
Add them to a list:
var list = new List<WrapperObject>();
list.Add(new WrapperObject() {
Name = "RedHat",
Resource = Properties.Resources.RedHat
});
..randomize:
var rnd = new Random();
list = list.OrderBy(x => rnd.Next(50)).ToList();
Any specific reason why you want them in two lists, you could just create a list of keyvaluepairs like this:
var list = new List<KeyValuePair<string, image>> ();
list.Add(new KeyValuePair<string, image>("RedHat", (Properties.Resources.RedHat)));
list.Add(new KeyValuePair<string, image>("BlueHat", (Properties.Resources.BlueHat)));
list.Add(new KeyValuePair<string, image>("YellowHat", (Properties.Resources.YellowHat)));
You could store the data in a Tuple<,> but if you had more than 2 elements its worth just creating an explicit class to store the data.
Tuple example:
List<Tuple<string, image>> list = new List<Tuple<string, image>>();
list.Add(new Tuple<string,image>("RedHat", Properties.Resources.RedHat));
// etc...
LINQ-fu version:
var rng = new Random();
var res = Enumerable.Zip(list, list2, (e1, e2) => new { e1, e2 })
.OrderBy(x => rng.Next())
.Aggregate(new { list1 = new List<string>(), list2 = new List<image>() },
(lists, next) =>
{
lists.list1.Add(next.e1);
lists.list2.Add(next.e2);
return lists;
});
list = res.list1;
list2 = res.list2;
The following code should do what you want:
var list1 = new List<string>
{
"RedHat",
"BlueHat",
"YellowHat"
};
var list2 = new List<int>
{
1,
2,
3
};
var combined = list1.Zip(list2, (a, b) => new { a, b }).Shuffle(new Random()).ToList();
list1 = combined.Select(i => i.a).ToList();
list2 = combined.Select(i => i.b).ToList();
You'll need the following extension method:
public static class ShuffleExtension
{
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
T[] elements = source.ToArray();
for (int i = elements.Length - 1; i >= 0; i--)
{
int swapIndex = rng.Next(i + 1);
yield return elements[swapIndex];
elements[swapIndex] = elements[i];
}
}
}
First put the corresponding elements together, then apply random order:
var rnd = new Random();
var ordered = list.Zip(list2, Tuple.Create).OrderBy(el => rnd.Next()).ToArray();
You can easily extract back the individual lists, if needed:
var ordered_list = ordered.Select(tuple => tuple.Item1).ToList();
var ordered_list2 = ordered.Select(tuple => tuple.Item2).ToList();
So basically I have the following 2 IEnumerable lists
List A = {"Personal", "Tech", "Social"}
List B = {"Personal", "Tech", "General"}
Now what I want to achieve is, get the difference between List A and List B, in this case Social and General.
I also need to determine that Social is extra in List A and General is extra in List B to insert and delete accordingly.
I can also have another scenario
List A = {"Personal", "Tech"}
List B = {"Personal", "Tech", "General"}
in this case it would return General"
How can I do that with LINQ?
Here you go
var ListA = new List<string> {"Personal", "Tech", "Social"};
var ListB = new List<string> { "Personal", "Tech", "General" };
var insert = ListA.Except(ListB).ToList();
var delete = ListB.Except(ListA).ToList();
You can use List<T>.Except() Method.
Produces the set difference of two sequences.
public static void Main(string[] args)
{
List<string> A = new List<string> { "Personal", "Tech", "Social" };
List<string> B = new List<string> { "Personal", "Tech", "General" };
var result = A.Except(B);
//Will print "Social"
foreach (var i in result)
{
Console.WriteLine(i);
}
}
Here is a DEMO.
For your second case;
public static void Main(string[] args)
{
List<string> A = new List<string> { "Personal", "Tech" };
List<string> B = new List<string> { "Personal", "Tech", "General"};
var result = B.Except(A);
foreach ( var i in result )
{
Console.WriteLine(i);
}
}
Here is a DEMO.
listA.Except(listB) will give you all of the items in list A that are not in list B.
Than do the reverse.
Use Enumerable.Except
var result = list1.Except(list2).ToList();
var q = A.Intersect(B);//"Personal" , "Tech"
var r = B.Except(A);//"General"