Intersect Two Lists in C# - c#

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

Filter a list of address objects by a list of string postcodes [duplicate]

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();

C#: How do i get 2 lists into one 2-tuple list in

I have 2 Lists. First one is Type string. The second is type object. Now I want to get both lists into a Tuple<string,object>.
like this
var List = new(string list1, object list2)[]
How do I do this?
I had to create 2 seperate lists, because I am Serializing the object and the serializing wouldn't work if i had a List<Tuple<string,object>> in the first place.
Both lists can get big so maybe with foreach loop?
You can use the Zip method to create one list of the two:
var lst = new List<string>() { "a", "b" };
var obj = new List<object>() { 1, 2 };
var result = lst.Zip(obj, (x, y) => new Tuple<string, object>(x, y))
.ToList();
You can use the Linq Zip method:
List<string> list1 = GetStrings();
List<object> list2 = GetObjects();
var merged = list1.Zip(list2, (a, b) => Tuple.Create(a, b));

compare multiple arraylist lengths to find longest one

I have 6 array lists and I would like to know which one is the longest without using a bunch of IF STATEMENTS.
"if arraylist.count > anotherlist.count Then...." <- Anyway to do this other than this?
Examples in VB.net or C#.Net (4.0) would be helpfull.
arraylist1.count
arraylist2.count
arraylist3.count
arraylist4.count
arraylist5.count
arraylist6.count
DIM longest As integer = .... 'the longest arraylist should be stored in this variable.
Thanks
Is 1 if statement acceptable?
public ArrayList FindLongest(params ArrayList[] lists)
{
var longest = lists[0];
for(var i=1;i<lists.Length;i++)
{
if(lists[i].Length > longest.Length)
longest = lists[i];
}
return longest;
}
You could use Linq:
public static ArrayList FindLongest(params ArrayList[] lists)
{
return lists == null
? null
: lists.OrderByDescending(x => x.Count).FirstOrDefault();
}
If you just want the length of the longest list, it's even simpler:
public static int FindLongestLength(params ArrayList[] lists)
{
return lists == null
? -1 // here you could also return (int?)null,
// all you need to do is adjusting the return type
: lists.Max(x => x.Count);
}
If you store everything in a List of Lists like for example
List<List<int>> f = new List<List<int>>();
Then a LINQ like
List<int> myLongest = f.OrderBy(x => x.Count).Last();
will yield the list with the most number of items. Of course you will have to handle the case when there is tie for the longest list
SortedList sl=new SortedList();
foreach (ArrayList al in YouArrayLists)
{
int c=al.Count;
if (!sl.ContainsKey(c)) sl.Add(c,al);
}
ArrayList LongestList=(ArrayList)sl.GetByIndex(sl.Count-1);
If you just want the length of the longest ArrayList:
public int FindLongest(params ArrayList[] lists)
{
return lists.Max(item => item.Count);
}
Or if you don't want to write a function and just want to in-line the code, then:
int longestLength = (new ArrayList[] { arraylist1, arraylist2, arraylist3,
arraylist4, arraylist5, arraylist6 }).Max(item => item.Count);

How can I convert comma separated string into a List<int> [duplicate]

This question already has answers here:
Convert comma separated string of ints to int array
(9 answers)
Closed 1 year ago.
string tags = "9,3,12,43,2"
List<int> TagIds = tags.Split(',');
This doesn't work cause the split method returns a string[]
Here is one way of doing it:
List<int> TagIds = tags.Split(',').Select(int.Parse).ToList();
If you want to include some simple validation and skip over invalid values (instead of throwing an exception), here's something that uses TryParse:
string csv = "1,2,3,4,a,5";
int mos = 0;
var intList = csv.Split(',')
.Select(m => { int.TryParse(m, out mos); return mos; })
.Where(m => m != 0)
.ToList();
//returns a list with integers: 1, 2, 3, 4, 5
EDIT: Here is an updated query based on feedback by Antoine. It calls TryParse first to filter out any bad values, and then Parse to do the actual conversion.
string csv = "1,2,3,4,a,5,0,3,r,5";
int mos = 0;
var intList = csv.Split(',')
.Where(m => int.TryParse(m, out mos))
.Select(m => int.Parse(m))
.ToList();
//returns a list with integers: 1, 2, 3, 4, 5, 0, 3, 5
Edit 2: An updated query for C# 7.0, thanks to feedback from Charles Burns. Note that we get rid of the extra mos variable with this approach, so it's a bit cleaner.
string csv = "1,2,3,4,a,5,0,3,r,5";
var intList = csv.Split(',')
.Where(m => int.TryParse(m, out _))
.Select(m => int.Parse(m))
.ToList();
You can use LINQ w/ int.Parse() to convert the string[] to an IEnumerable<int> and then pass that result to the List<T> constructor:
var tagIds = new List<int>(tags.Split(',').Select(s => int.Parse(s)));
A little LINQ goes a long way:
List<int> TagIds = tags.Split(',')
.Select(t => int.Parse(t))
.ToList();
Without LINQ Query , you can choose this method ,
string tags = "9,3,12,43,2";
List<string> numbers = nos.Split(',').ToList<string>();
and then you can convert this List into integer type...
string tags = "9,3,12,43,2"
List<int> TagIds = tags.Split(',').Select(x => x.Trim()).Select(x=> Int32.Parse(x)).ToList();
If you are using C# 3.5 you can use Linq to achieve this
string tags = "9,3,12,43,2";
List<int> tagIds = tags.Split(',').Select(s=>int.Parse(s)).ToList();
or the short one
string tags = "9,3,12,43,2";
List<int> tagIds = tags.Split(',').Select(int.Parse).ToList();
string tags = "9,3,12,43,2";
List<int> TagIds = tags.Split(',').Select(int.Parse).ToList();
I stumbled upon this and I just want to share my own solution without linq. This is a primitive approach. Non-integer values will not be added in the list also.
List<int> TagIds = new List<int>();
string[] split = tags.Split(',');
foreach (string item in split)
{
int val = 0;
if (int.TryParse(item, out val) == true)
{
TagIds.Add(val);
}
}
Hope this helps.
I made a modification to khalid13's answer. If the user put a string of "0", his answer would remove that from the resulting list. I did something similar but used an anonymous object.
var result = commaSeparatedString.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => new { didConvert = int.TryParse(s.TrimNullProtection(), out convertedInt), convertedValue = convertedInt })
.Where(w => w.didConvert)
.Select(s => s.convertedValue)
.ToList();
TrimNullProtection is a custom function I made that protects if the string is null.
What the above does is strip out any strings that were not able to be converted with no error. If you need to error if there was a problem with the conversion, then the accepted answer should do the trick.
It's simple. First split the string.
Trim blank space present after comma(,).
Then use system defined ToList()
string inputText = "text1, text2"
To remove the space after ',' and convert this comma separated text to List
List<string> resultList = (inputText.Split(',')).Select(t => t).ToList();

Using lambda expressions to get a subset where array elements are equal

I have an interesting problem, and I can't seem to figure out the lambda expression to make this work.
I have the following code:
List<string[]> list = GetSomeData(); // Returns large number of string[]'s
List<string[]> list2 = GetSomeData2(); // similar data, but smaller subset
List<string[]> newList = list.FindAll(predicate(string[] line){
return (???);
});
I want to return only those records in list in which element 0 of each string[] is equal to one of the element 0's in list2.
list contains data like this:
"000", "Data", "more data", "etc..."
list2 contains data like this:
"000", "different data", "even more different data"
Fundamentally, i could write this code like this:
List<string[]> newList = new List<string[]>();
foreach(var e in list)
{
foreach(var e2 in list2)
{
if (e[0] == e2[0])
newList.Add(e);
}
}
return newList;
But, i'm trying to use generics and lambda's more, so i'm looking for a nice clean solution. This one is frustrating me though.. maybe a Find inside of a Find?
EDIT:
Marc's answer below lead me to experiment with a varation that looks like this:
var z = list.Where(x => list2.Select(y => y[0]).Contains(x[0])).ToList();
I'm not sure how efficent this is, but it works and is sufficiently succinct. Anyone else have any suggestions?
You could join? I'd use two steps myself, though:
var keys = new HashSet<string>(list2.Select(x => x[0]));
var data = list.Where(x => keys.Contains(x[0]));
If you only have .NET 2.0, then either install LINQBridge and use the above (or similar with a Dictionary<> if LINQBridge doesn't include HashSet<>), or perhaps use nested Find:
var data = list.FindAll(arr => list2.Find(arr2 => arr2[0] == arr[0]) != null);
note though that the Find approach is O(n*m), where-as the HashSet<> approach is O(n+m)...
You could use the Intersect extension method in System.Linq, but you would need to provide an IEqualityComparer to do the work.
static void Main(string[] args)
{
List<string[]> data1 = new List<string[]>();
List<string[]> data2 = new List<string[]>();
var result = data1.Intersect(data2, new Comparer());
}
class Comparer : IEqualityComparer<string[]>
{
#region IEqualityComparer<string[]> Members
bool IEqualityComparer<string[]>.Equals(string[] x, string[] y)
{
return x[0] == y[0];
}
int IEqualityComparer<string[]>.GetHashCode(string[] obj)
{
return obj.GetHashCode();
}
#endregion
}
Intersect may work for you.
Intersect finds all the items that are in both lists.
Ok re-read the question. Intersect doesn't take the order into account.
I have written a slightly more complex linq expression that will return a list of items that are in the same position (index) with the same value.
List<String> list1 = new List<String>() {"000","33", "22", "11", "111"};
List<String> list2 = new List<String>() {"000", "22", "33", "11"};
List<String> subList = list1.Select ((value, index) => new { Value = value, Index = index})
.Where(w => list2.Skip(w.Index).FirstOrDefault() == w.Value )
.Select (s => s.Value).ToList();
Result: {"000", "11"}
Explanation of the query:
Select a set of values and position of that value.
Filter that set where the item in the same position in the second list has the same value.
Select just the value (not the index as well).
Note I used:
list2.Skip(w.Index).FirstOrDefault()
//instead of
list2[w.Index]
So that it will handle lists of different lengths.
If you know the lists will be the same length or list1 will always be shorter then list2[w.Index] would probably a bit faster.

Categories