check if an array contains values of another array - c#

I have two list, I want the values of list 1 if it contains any of value from list 2.
List<string> list1 = new List<string>();
list1.Add("Sunday is far away");
list1.Add("Today is Monday");
list1.Add("Tuesday is too near");
List<string> list2 = new List<string>();
list2.Add("Sunday");
list2.Add("Monday");
list2.Add("Tuesday");
var result1 = list1.Where(x => list2.Any(y => y.Contains(x))).ToList(); //no results
var result2 = list2.Where(x => list1.Any(y => y.Contains(x))).ToList(); //give values of list2. But I need values of list1
Update:
I need values of list1 in result, how can I get that?

Simple thing you missed, Take a look into the collection, All Items in the first list are larger than that of second, so the contains will return false. So you have to check for second item in first like the following:
Here is your modified code with result:
var result1 = list1.Where(x => list2.Any(y => x.Contains(y))).ToList();
var result2 = list2.Where(x => list1.Any(y => y.Contains(x))).ToList();

Simply you can. If List1 contains any value of List2 then result=List1. Otherwise null;
var result = list2.Any(l2 => list1.Contains(l2))==true?list1:null;

Related

Searching a List of List

I have the following object:
List<List<MyObj>> lst;
I need to find a list of all the objects (List< MyObj >) in the inner list, that has ID equal 1.
I tried:
lst.Where(x => x.FindAll(y=> y.ID== "1"));
lst.FindAll(x => x.FindAll(y=> y.ID== "1"));
and also tried to use Any() but no luck.
You can use SelectMany() to flatten the lists and then filter the elements:
var result = lst.SelectMany(x => x).Where(y => y.ID == "1").ToList();
List<MyObj> list1 = lst.SelectMany(x => x.Where(y=> y.ID== "1")).ToList();
or
List<List<MyObj>> list2 = lst.Where(x => x.Any(y=> y.ID== "1")).ToList();
depending on what it is you want as a result..
SelectMany is your friend. Example:
var listOfListsOfStrings = new List<List<string>>();
listOfListsOfStrings.Add(new List<string>() {"a", "b"});
listOfListsOfStrings.Add(new List<string>() {"c", "d"});
var allStrings = listOfListsOfStrings.SelectMany(s => s);
Console.WriteLine(string.Join(", ", allStrings.ToArray())); //prints: a, b, c, d
So in your case you just need:
lst.SelectMany(x => x).Where(y => y.ID == "1")
Let me add another option to the already good set of options. It is using Hashset<T> for search, by converting the internal List<T>, this would help when data size is more, since Hashset<T> has O(1) search instead of O(N) for List<T>
List<List<MyObj>> lst;
var result = lst.where(x =>
{
// Assuming ID to be string type
var hashset = new Hashset<string>(x.Select(y => y.ID));
return hashset.Contains("1");
}
);
In case you are not keen to do conversion, then following code would do:
var result = lst.where(x => x.Any(y => y.ID == "1"));
result will be of type List<List<MyObj>>, which will be filtered, currently we are are supplying Func<T,bool> to the Enumerable.Where, when data is supplied at run-time, then its easier to construct Expression<Func<T,bool>>, which gets compiled at run-time into correct Func<T,bool> delegate to filter actual list

Sorting a dynamic object list based on another list

I have two lists. One is a dynamic list of objects. Another is a list of strings. I want to sort the object list based on the other list.
List<dynamic> List1; // Object1,Object2,Object3,Object4
List <String> List2; //"abc","bcd","da"
These objects has one of the attributes "alphabets" on whose basis it has to be sorted.
The objects may not be equal to number of elements in second list.
Something like this might work, if the indexes of the two lists align how you want them to. You'd have to ensure that the lists have the same length for this to work correctly though.
var result = list1
.Select((item, index) =>
new
{
Item = item,
Order = list2[index]
})
.OrderBy(x => x.Order)
.Select(x => x.Item);
If they aren't the same length, what would be the criteria for the order? That would be an undefined problem. One approach would be to put them at the end of the list.
var result = list1.Take(list2.Length)
.Select((item, index) =>
new
{
Item = item,
Order = list2[index]
})
.OrderBy(x => x.Order)
.Select(x => x.Item);
var concatted = result.Concat(list1.Skip(list2.Length));
Ok, assuming that List1 contains a list of objects, and each object contains an attribute called "alphabet", and you want to sort this list of objects, but the sort order is specified in List2 which has the possible values of alphabet in sorted order, then you could do this:
int i=0;
var List2WithRowNum = from str2 in List2.AsEnumerable() select new{ rowNum = i++, str2 };
var sortedList = from obj1 in List1.AsEnumerable()
join strKey in List2WithRowNum.AsEnumerable() on ((listObject)obj1).alphabet equals strKey.str2
orderby strKey.rowNum
select obj1;
sortedList would then be a list of your original objects (from List1) sorted by their "alphabet" attribute, in List2 order.

LINQ search though a list of string arrays for a particular string

I have a list of string arrays:
List<String[]> listOfStringArrays = something;
I need to select all objects from a collection that have a value which is equal to the string at the 0th index of any string array in the list.
For example, if I just had a simple list of strings, declared as:
List<String> listOfStrings = something;
I would just do:
var query = someCollection.Where(x => listOfStrings.Contains(x.id_num))
But obviously it's not as simple with a list of string arrays.
I know that I can easily just iterate through the list of string arrays and create a simple list of strings with the 0th value, like this:
List<String[]> listOfStringArrays = something;
List<String> listOfValues = new List<String>();
foreach (string[] s in listOfStringArrays)
listOfValues.Add(s[0]);
var query = someCollection.Where(x => listOfValues.Contains(x => x.id_num);
But would really like to avoid this and am trying to write it as a one liner without introducing extra lists and loops.
You can put it all into one query:
someCollection.Where(x => listOfValues.Select(y => y[0]).Contains(x => x.id_num);
But it will iterate over listOfValues over and over again.
I would rather go with HashSet<string> to make it faster:
var set = new HashSet<string>(listOfValues.Select(y => y[0]));
someCollection.Where(x => set.Contains(x));
Try the following
var query = someCollection.Where(s => listOfStringArrays.Any(a => a[0] == s));
var firsts = listOfString.Select(x => x[0]);
var query = someCollection.Where(x => firsts.Contains(x));
This will project each array to it's first element, and then match from there
As a one liner:
var query = someCollection.Where(x => listOfString.Select(y => y[0]).Contains(x));
It should be simply:
List<String[]> newListOfStrings = listOfStrings.where(x => x[0].Contains(identifer)).ToList()
The final ToList is needed in this case because I have not used var.

Modifying an IEnumerable type

I have a a string IEnumerable type that I get from the below code.The var groups is an Enumerable type which has some string values. Say there are 4 values in groups and in the second position the value is just empty string "" .The question is how can I move it to the 4th ie the end position.I do not want to sort or change any order.Just move the empty "" value whereever it occurs to the last position.
List<Item> Items = somefunction();
var groups = Items.Select(g => g.Category).Distinct();
Simply order the results by their string value:
List<Item> Items = somefunction();
var groups = Items.Select(g => g.Category).Distinct().OrderByDescending(s => s);
Edit (following OP edit):
List<Item> Items = somefunction();
var groups = Items.Select(g => g.Category).Distinct();
groups = groups.Where(s => !String.IsNullOrEmpty(s))
.Concat(groups.Where(s => String.IsNullOrEmpty(s)));
You can't directly modify the IEnumerable<> instance, but you can create a new one:
var list = groups.Where(x => x != "").Concat(groups.Where(x => x == ""));
Note that in this query, groups is iterated twice. This is usually not a good practice for a deferred IEnumerable<>, so you should call ToList() after the Distinct() to eagerly evaluate your LINQ query:
var groups = Items.Select(g => g.Category).Distinct().ToList();
EDIT :
On second thought, there's a much easier way to do this:
var groups = Items.Select(g => g.Category).Distinct().OrderBy(x => x == "");
Note that this doesn't touch the order of the non-empty elements since OrderBy is stable.
var groups = Items.Select(g => g.Category).Distinct().OrderByDescending(s =>s);
I don't like my query but it should do the job. It selects all items which are not empty and unions it with the items which are empty.
var groups = Items.Select(g => g.Category).Distinct()
.Where(s => !string.IsNullOrEmpty(s))
.Union(Items.Select(g => g.Category).Distinct()
.Where(s => string.IsNullOrEmpty(s)));
Try something like
var temp = groups.Where(item => ! String.IsNullOrEmpty(item)).ToList<string>();
while (temp.Count < groups.Count) temp.Add("");

Searching a list to get index values in c#

I have a string comma separated list of some data. I have another list of strings of keywords that i want to search for in the first list. I want to have returned to me the index of all the elements in the first list that do no contain any of the keywords in the second list. For example:
List 1:
Student,101256,Active
Professor,597856,Active
Professor,697843,Inactive
Student,329741,Active
Student,135679,Inactive
Student,241786,Inactive
List 2:
697843
241786
My query on List 1 should be, give me all the index of all the elements that do not contain any of the elements of list 2. Therefore, the return list of indices should be 0,1,3,4. Is there any way to accomplish this?
Thanks in advance!
Edit: This is my try:
List<int> index = list1
.Select((s, i) => new { s, i })
.Where(e => !list2.Contains(e.s))
.Select(e => e.i).ToList();
You will need to reference System.Linq, this has now been edited to include the !Student filter
var list1 = new List<string> {
{"Student,101256,Active"},
{"Professor,597856,Active"},
{"Professor,697843,Inactive"},
{"Student,329741,Active"},
{"Student,135679,Inactive"},
{"Student,241786,Inactive"}
};
var list2 = new List<string> {{"697843"}, {"241786"}};
var result = list1
.Select((item,i)=> new {index=i,value=item})
.Where(item => !item.value.StartsWith("Student"))
.Where(item => !item.value.Split(',').Any(j => list2.Contains(j)))
.Select(item=>item.index)
.ToList();
The first select extracts the index before filtering, the pre-edited version calculated the index after the filter and so was incorrect.

Categories