I have two lists:
List<int> data1 = new List<int> {1,2,3,4,5};
List<string> data2 = new List<string>{"6","3"};
I want do to something like
var newData = data1.intersect(data2, lambda expression);
The lambda expression should return true if data1[index].ToString() == data2[index]
You need to first transform data1, in your case by calling ToString() on each element.
Use this if you want to return strings.
List<int> data1 = new List<int> {1,2,3,4,5};
List<string> data2 = new List<string>{"6","3"};
var newData = data1.Select(i => i.ToString()).Intersect(data2);
Use this if you want to return integers.
List<int> data1 = new List<int> {1,2,3,4,5};
List<string> data2 = new List<string>{"6","3"};
var newData = data1.Intersect(data2.Select(s => int.Parse(s));
Note that this will throw an exception if not all strings are numbers. So you could do the following first to check:
int temp;
if(data2.All(s => int.TryParse(s, out temp)))
{
// All data2 strings are int's
}
If you have objects, not structs (or strings), then you'll have to intersect their keys first, and then select objects by those keys:
var ids = list1.Select(x => x.Id).Intersect(list2.Select(x => x.Id));
var result = list1.Where(x => ids.Contains(x.Id));
From performance point of view if two lists contain number of elements that differ significantly, you can try such approach (using conditional operator ?:):
1.First you need to declare a converter:
Converter<string, int> del = delegate(string s) { return Int32.Parse(s); };
2.Then you use a conditional operator:
var r = data1.Count > data2.Count ?
data2.ConvertAll<int>(del).Intersect(data1) :
data1.Select(v => v.ToString()).Intersect(data2).ToList<string>().ConvertAll<int>(del);
You convert elements of shorter list to match the type of longer list. Imagine an execution speed if your first set contains 1000 elements and second only 10 (or opposite as it doesn't matter) ;-)
As you want to have a result as List, in a last line you convert the result (only result) back to int.
public static List<T> ListCompare<T>(List<T> List1 , List<T> List2 , string key )
{
return List1.Select(t => t.GetType().GetProperty(key).GetValue(t))
.Intersect(List2.Select(t => t.GetType().GetProperty(key).GetValue(t))).ToList();
}
Related
Below is the code:
string[] values = Acode.Split(',');
IEnumerable<Test> tst = null;
foreach (string a in values)
{
if (tst== null)
tst = entities.Test.Where(t=> (t.TCode == Convert.ToInt16(a)));
else
tst.Concat(entities.Test.Where(g => (g.TCode == Convert.ToInt16(a))));
}
return tst.ToList();
I am not able to get all the records in tst, it is giving me records only for the last value in array.
So if my array contains 1,2,3,4 I am getting records only for the 4. Whereas i need all the result for 1,2,3 and 4 get appended in tst.
Any help will be appreciated.
Concat doesn't modify anything - it returns a new sequence, which you're currently ignoring.
However, rather than using Concat, you should just use SelectMany to flatten the sequence:
string[] values = Acode.Split(',');
return values.SelectMany(a => entities.Test.Where(t => t.TCode == Convert.ToInt16(a)))
.ToList();
Or more efficiently, convert values into a List<short> and then you can do one query:
List<short> values = Acode.Split(',').Select(x => short.Parse(x)).ToList();
return entities.Test.Where(t => values.Contains(t.TCode)).ToList();
That is because Concat will return a new instance of your enumerable.
Either use in your else :
tst = tst.Concat(...)
Or Change your Enumerable into list from the beginning :
string[] values = Acode.Split(',');
List<Test> tst= new List<Test>;
foreach (string a in values)
{
tst.AddRange(entities.Test.Where(g => (g.TCode == Convert.ToInt16(a))));
}
return tst;
For simple example, I have a class like this:
public class MyClass {
public string name;
public int item1;
public int item2;
}
and a List(MyClass), how do I query the list to create List(int) of both fields item1 and item2? It seems it should be simple but I am struggling a long time.
var result = myList.Select(i => i.item1).ToList() //selects only one field
I know i could use anonymous type but since both item1 and item2 are integers I dont need any new type.
var result = myList.Select(i => new { i.item1, i.item2} ).ToList() //dont need new type, both are integers
how to create list of int? Or did I misunderstood what the anonymous types do?
If you don't like using anonymous types, you could always use a Tuple
var result = myList.Select(i => Tuple.Create(i.item1, i.item2) )
But since both item1 and item2 are integers, you can use an array:
var result = myList.Select(i => new[] { i.item1, i.item2 } )
This will result in a IEnumerable<int[]>. If you want an IEnumerable<int> (with each record's item1 and item2 together in one result set), use SelectMany:
var result = myList.SelectMany(i => new[] { i.item1, i.item2 } )
If you want a flattened list you can do:
List<int> ints = myList.SelectMany(i => new[] { i.item1, i.item2 }).ToList();
if you want to keep the values together you can create a tuple:
List<Tuple<int, int>> pairs = myList.Select(i => Tuple.Create(i.item1, i.item)).ToList():
You can use the following expression for example to create a list containing both items fro all elements of the original list myList.
var result = myList.Select(i => item1).Concat(myList.Select(i => i.item2));
List<List<String>> ls = new List<List<String>>();
List<String> l1 = new List<String>();
l1.Add("Peter");
l1.Add("123");
ls.Add(l1);
List<String> l2 = new List<String>();
l2.Add("Peter");
l2.Add("123");
ls.Add(l2);
ls = ls.Distinct().ToList();
I suppose there are only one element in ls, but actually there are still 2 elements. What are the possible reasons?
That's because List<T> has no Equals and GetHashCode implemented, so standard reference comparison is being performed. And it returns false, because you have two separated lists.
You can write your own IEqualityComparer<List<string>> implementation and provide it as Distinct method parameter. Within the comparer you can use Enumerable.SequenceEqual) method to check if lists has the same content.
With your case, you have to build the custom comparer to implement the interface IEqualityComparer<List<string>>, and use SequenceEqual to compare in Equal method:
public class CustomComparer : IEqualityComparer<List<string>>
{
public bool Equals(List<string> x, List<string> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(List<string> obj)
{
int hashCode = 0;
foreach (string str in obj)
{
hashCode ^= str.GetHashCode();
}
return hashCode;
}
}
Then:
ls = ls.Distinct(new CustomComparer()).ToList();
Another tricky way to distinct by using GroupBy:
ls = ls.GroupBy(x => string.Join("", x))
.Select(g => g.First())
.ToList();
The Comparison used by List is based on reference comparison. Since the 2 lists are different instances, they are not the same and distinct considers them to be different.
If you want the distinct values, you can use .SelectMany() to select the strings in each list within the parent list:
var list = new List<List<String>>();
var list1 = new List<String>();
list1.Add("Peter");
list1.Add("123");
list.Add(list1);
var list2 = new List<String>();
list2.Add("Peter");
list2.Add("123");
list.Add(list2);
var distinct = list.SelectMany(x => x).Distinct().ToList();
distinct.ForEach(x => Console.WriteLine(x));
I have a linq list obtained from database in my Model. Now I have a string array obtained from my controller. I want to construct a statement
pseudo-code
List<object> objlist = db.objects.tolist();
string[] strarray; // obtained from a long code.
var k = objlist.Where(u => u.somecol == strarray[0] || u.somecol == strarray[1]........strarray[n]).toList();
I am little bit confused how to accomplish this since my strarray[] is variable length and can contain upto 1000 words.
You can check if an array contains some item using the Array.IndexOf<T> Method:
bool strarrayContainsX = Array.IndexOf<string>(strarray, "X") >= 0;
However, I'd recommend you use a HashSet<string> instead of a string array for anything more than a few items. The HashSet<T> Class provides a Contains Method to check if a set contains some item:
HashSet<string> strset = new HashSet<string>(strarray);
bool strsetContainsX = strset.Contains("X");
The resulting query then looks like this:
var k = objlist.Where(u => strset.Contains(u.somecol)).ToList();
Use Contains:
var k = objlist.Where(u => strarray.Contains(u.somecol)).toList();
Try this:
List<object> objlist = db.objects.tolist();
string[] strarray; // obtained from a long code.
var k = objlist.Where(u => strarray.Contains(u.somecol)).toList();
var k = objlist.Where(u => strarray.Any(x=>x == u.somecol)).ToList();
I have a list of parameters like this:
public class parameter
{
public string name {get; set;}
public string paramtype {get; set;}
public string source {get; set;}
}
IEnumerable<Parameter> parameters;
And a array of strings i want to check it against.
string[] myStrings = new string[] { "one", "two"};
I want to iterate over the parameter list and check if the source property is equal to any of the myStrings array. I can do this with nested foreach's but i would like to learn how to do it in a nicer way as i have been playing around with linq and like the extension methods on enumerable like where etc so nested foreachs just feel wrong. Is there a more elegant preferred linq/lambda/delegete way to do this.
Thanks
You could use a nested Any() for this check which is available on any Enumerable:
bool hasMatch = myStrings.Any(x => parameters.Any(y => y.source == x));
Faster performing on larger collections would be to project parameters to source and then use Intersect which internally uses a HashSet<T> so instead of O(n^2) for the first approach (the equivalent of two nested loops) you can do the check in O(n) :
bool hasMatch = parameters.Select(x => x.source)
.Intersect(myStrings)
.Any();
Also as a side comment you should capitalize your class names and property names to conform with the C# style guidelines.
Here is a sample to find if there are match elements in another list
List<int> nums1 = new List<int> { 2, 4, 6, 8, 10 };
List<int> nums2 = new List<int> { 1, 3, 6, 9, 12};
if (nums1.Any(x => nums2.Any(y => y == x)))
{
Console.WriteLine("There are equal elements");
}
else
{
Console.WriteLine("No Match Found!");
}
If both the list are too big and when we use lamda expression then it will take a long time to fetch . Better to use linq in this case to fetch parameters list:
var items = (from x in parameters
join y in myStrings on x.Source equals y
select x)
.ToList();
list1.Select(l1 => l1.Id).Intersect(list2.Select(l2 => l2.Id)).ToList();
var list1 = await _service1.GetAll();
var list2 = await _service2.GetAll();
// Create a list of Ids from list1
var list1_Ids = list1.Select(l => l.Id).ToList();
// filter list2 according to list1 Ids
var list2 = list2.Where(l => list1_Ids.Contains(l.Id)).ToList();