So I've got the following code:
string matchingName = "Bob";
List<string> names = GetAllNames();
if (names.Contains(matchingName))
// Get the index/position in the list of names where Bob exists
Is it possible to do this with a couple of lines of code, rather than iterating through the list to get the index or position?
If you have multiple matching instances and want to get all the indices you can use this:
var result = Enumerable.Range(0, names.Count).Where(i => names[i] == matchingName);
If it is just one index you want, then this will work:
int result = names.IndexOf(matchingName);
If there is no matching instance in names, the former solution will yield an empty enumeration, while the latter will give -1.
var index = names.IndexOf(matchingName);
if (index != -1)
{
// do something with index
}
If you want to look for a single match, then IndexOf will suit your purposes.
If you want to look for multiple matches, consider:
var names = new List<string> {"Bob", "Sally", "Hello", "Bob"};
var bobIndexes = names.Select((value, index) => new {value, index})
.Where(z => z.value == "Bob")
.Select(z => z.index);
Console.WriteLine(string.Join(",", bobIndexes)); // this outputs 0,3
The use of (value, index) within Select gives you access to both the element and its index.
Related
We have the list A which contain the random indexes in it. I have another list B which contains the class objects in it. I want to parse list B with the indexes present in list A and find the objects which have name Amar in it using Linq.
For example:
List<int> Indexes = new List<int>(); // This contains the random indexes
List<Student> StuObj = new List<Student>();
Class Student
{
String name;
}
Now I want to parse the list StuObj with the respect to the indexes present in the list Indexes and get the Student object indexes present in the list StuObj where the name is Amar.
You can do that using Linq. The Where has an overload that provides the index of the element:
List<int> indexes = new List<int>() { 5, 1 , 10, 30 };
var results = listB.Where((item, index)=> indexes.Contains(index)
&& item.Name == "Amar")
.Select(x => listB.IndexOf(x)).ToList();
Edit: to get the index of the element in the original listB, you can make use of the IndexOf(T) method.
This should work:
var result = Indexes
.Select(i => StuObj[i])
.Where(s => s.name = "Amar").ToList()
It performs fast index lookup to fetch only required objects. If you know there is only one record, you can use First or FirstOrDefault instead of ToList.
Assuming you have two lists, List indexList and List dataList, you can use select as follows:
indexList.Select(i => dataList[i]);
You should consider what you wish to happen if indexList contains an integer < 0 or > the size of dataList. For example, you could replace invalid entries with null, like:
indexList.Select(i => i < 0 || i >= dataList.Count ? null : dataList[i]);
Or you could filter them out like:
indexList.Where(i => i>=0 && i < dataList.Count).Select(i => dataList[i]);
Or you may know via preconditions that you will never have items in the index list that are out of the range of valid values.
EDIT
Based on the updated question, try this:
dataList.Where((item, index) => indexList.Contains(index) && item.Name == "Amar")
.Select(item => dataList.IndexOf(item));
This takes advantage of the Select and Where overloads that take the index of the item. The Where clause selects the item where the item's index in dataList is in the indexList, and also where the item's Name is Amar. The Select clause then returns the index of the item in the original data list.
Something like this:
var result = listA
.Where(i => i >= 0 && i < listB.Count)
.Select(i => listB[i])
.FirstOrDefault(b => b.Name == "Amar");
Basically you use the value from listA as index of an element of the listB. If you are sure that listA contains only valid indexes, then Where call can be removed.
EDIT: As per updated question, the answer is even easier:
var result = listA
.Where(i => i >= 0 && i < listB.Count && listB[i].Name == "Amar")
.ToList();
I see absolutely no reason to use the linear searching (hence slow) methods Contains and IndexOf as suggested in some other answers.
i have a list say :
List<string> list = new List<string>(){"Oracle","SQL Server","Java","Oracle","Python"};
Now i'm trying to get index of the second "Oracle" from the list using LINQ:
var indexFirefox = list.FindIndex(a => a.Text == "Oracle");
But this will give you only the first instance that is index 0. I want index as 4 in my result. Will using Skip help or there is a more simplistic way of getting the index. The example above is just a demo, i have a list with over 300 values.
So many Linq answer when there already exists one method doing the job (given in the first comment)
List<T>.FindIndex has an overload which takes an additional index parameter to search only from that index.
So to get the second occurrence of an item you just have to use that overload with the result of a "regular" FindIndex.
(Note: in your question's sample you used a.Text but items are string so there is no Text property)
var firstIndex = list.FindIndex (item => item == "Oracle");
if (firstIndex != -1)
{
var secondIndex = list.FindIndex (firstIndex, item => item == "Oracle");
// use secondIndex (can be -1 if there is no second occurrence)
}
And with your sample example secondIndex will be 3 (and not 4 as stated in your question, indexes are zero-based)
Alternatively if you want to get occurrence indexes in turn you can loop using that same method.
// defined here to not have to repeat it
Predicate<string> match = item => item == "Oracle";
for (var index = list.FindIndex (match); index > -1; index = list.FindIndex (index + 1, match))
{
// use index here
}
Or if you prefer a while loop
var index = list.FindIndex (match);
while (index > -1)
{
// use index here
index = list.FindeIndex (index + 1, match);
}
here i find index of next of matching element
list.Select((value, index) => new { value, index = index + 1 }).Where(x => x.value == "Oracle").Skip(1).FirstOrDefault().index
This does it nicely for me:
var result =
list
.Select((x, n) => new { x, n })
.Where(xn => xn.x == "Oracle")
.Select(xn => xn.n)
.Skip(1)
.FirstOrDefault();
This gives 3 given the input List<string> list = new List<string>(){"Oracle","SQL Server","Java","Oracle","Python"};.
list.Where(x => x == "Oracle").ElementAt(1);
Will crash if ther is no second find , you can just check the size of the enumerable before returning second element.
I want to sort a C# list by word. Assume I have a C# list (of objects) which contains following words:
[{id:1, name: "ABC"},
{id:2, name: "XXX"},
{id:3, name: "Mille"},
{id:4, name: "YYY"},
{id:5, name: "Mill",
{id:6, name: "Millen"},
{id:7, name: "OOO"},
{id:8, name: "GGGG"},
{id:9, name: null},
{id:10, name: "XXX"},
{id:11, name: "mil"}]
If user pass Mil as a search key, I want to return all the words starting with the search key & then all the words which does not match criteria & have them sort alphabetically.
Easiest way I can think of is to run a for loop over the result set, put all the words starting with search key into one list and put the renaming words into another list. Sort the second list and them combine both the list to return the result.
I wonder if there is a smarter or inbuilt way to get the desired result.
Sure! You will sort by the presence of a match, then by the name, like this:
var results = objects.OrderByDescending(o => o.Name.StartsWith(searchKey))
.ThenBy(o => o.Name);
Note that false comes before true in a sort, so you'll need to use OrderByDescending.
As AlexD points out, the name can be null. You'll have to decide how you want to treat this. The easiest way would be to use o.Name?.StartsWith(searchKey) ?? false, but you'll have to decide based on your needs. Also, not all Linq scenarios support null propagation (Linq To Entities comes to mind).
This should do it, but there's probably a faster way, maybe using GroupBy somehow.
var sorted = collection
.Where(x => x.Name.StartsWith(criteria))
.OrderBy(x => x.Name)
.Concat(collection
.Where(x => !x.Name.StartsWith(criteria))
.OrderBy(x => x.Name))
You can try GroupBy like this:
var sorted = collection
.GroupBy(item => item.Name.StartsWith(criteria))
.OrderByDescending(chunk => chunk.Key)
.SelectMany(chunk => chunk
.OrderBy(item => item.Name));
Separate items into two groups (meets and doesn't meet the criteria)
Order the groups as whole (1st that meets)
Order items within each group
Finally combine the items
There's nothing C#-specific to solve this, but it sounds like you're really looking for algorithm design guidance.
You should sort the list first. If this is a static list you should just keep it sorted all the time. If the list is large, you may consider using a different data structure (Binary Search Tree, Skip List, etc.) which is more optimized for this scenario.
Once it's sorted, finding matching elements becomes a simple binary search. Move the matching elements to the beginning of the result set, then return.
Add an indicator of a match into the select, and then sort on that:
void Main()
{
word[] Words = new word[11]
{new word {id=1, name= "ABC"},
new word {id=2, name= "XXX"},
new word {id=3, name= "Mille"},
new word {id=4, name= "YYY"},
new word {id=5, name= "Mill"},
new word {id=6, name= "Millen"},
new word {id=7, name= "OOO"},
new word {id=8, name= "GGGG"},
new word {id=9, name= null},
new word {id=10, name= "XXX"},
new word {id=11, name= "mil"}};
var target = "mil";
var comparison = StringComparison.InvariantCultureIgnoreCase;
var q = (from w in Words
where w.name != null
select new {
Match = w.name.StartsWith(target, comparison)?1:2,
name = w.name})
.OrderBy(w=>w.Match).ThenBy(w=>w.name);
q.Dump();
}
public struct word
{
public int id;
public string name;
}
It is probably not easier but you could create a class that implements IComparable Interface and have a property Mil that is used by CompareTo.
Then you could just call List.Sort(). And you can pass an IComparer to List.Sort.
It would probably be the most efficient and you can sort in place rather than producing a new List.
On average, this method is an O(n log n) operation, where n is Count;
in the worst case it is an O(n ^ 2) operation.
public int CompareTo(object obj)
{
if (obj == null) return 1;
Temperature otherTemperature = obj as Temperature;
if (otherTemperature != null)
{
if(string.IsNullOrEmpty(Mil)
return this.Name.CompareTo(otherTemperature.Name);
else if(this.Name.StartsWith(Mill) && otherTemperature.Name.StartsWith(Mill)
return this.Name.CompareTo(otherTemperature.Name);
else if(!this.Name.StartsWith(Mill) && !otherTemperature.Name.StartsWith(Mill)
return this.Name.CompareTo(otherTemperature.Name);
else if(this.Name.StartsWith(Mill))
return 1;
else
return 0;
}
else
throw new ArgumentException("Object is not a Temperature");
}
You will need to add how you want null Name to sort
First create a list of the words that match, sorted.
Then add to that list all of the words that weren't added to the first list, also sorted.
public IEnumerable<Word> GetSortedByMatches(string keyword, Word[] words)
{
var result = new List<Word>(words.Where(word => word.Name.StartsWith(keyword))
.OrderBy(word => word.Name));
result.AddRange(words.Except(result).OrderBy(word => word.Name));
return result;
}
Some of the comments suggest that it should be case-insensitive. That would be
public IEnumerable<Word> GetSortedByMatches(string keyword, Word[] words)
{
var result = new List<Word>(
words.Where(word => word.Name.StartsWith(keyword, true)) //<-- ignoreCase
.OrderBy(word => word.Name));
result.AddRange(words.Except(result).OrderBy(word => word.Name));
return result;
}
I have an array of integers. I can GroupBy, Sort, and Take the less repeat element:
int [] list = new int[] {0, 1, 1, 0, 2};
var result = list
.GroupBy(a => a)
.Select(g => new {
Number = g.Key,
Total = g.Count()
})
.OrderBy(g => g.Total).Take(1);
Inside a foreach I can retrieve the result, I even have Intellisense for group properties {Number, Total}
foreach (var group in result)
{
// Display key and its values.
Console.WriteLine("Number = {0} Total: {1}", group.Number, group.Total);
}
But I don't like to do a foreach to do that, I prefer to do something like
result.Number or
result[0].Number
But doesn't work. What should be the right way to do it?
You are using Take(1) which still returns an IEnumerable. You probably want to use FirstOrDefault() which does not return an IEnumerable.
.OrderBy(g => g.Total).Take(1);
Then you can use it like result.Number and result.Total.
Your problem is that Take returns IEnumerable, so if you want to fetch only first element, in that case use FirstOrDefault:-
var result = list.GroupBy(a => a)
.Select(g => new
{
Number = g.Key,
Total = g.Count()
})
.OrderBy(g => g.Total).FirstOrDefault();
Then, you can simply say: - result.Number. Please note FirstOrDefault may return null so better check for nulls before accessing any property otherwise you may get Null Reference Exception.
Also, if you are looking for any specific index then you can use ElementAtOrDefault like this:-
var result = list.GroupBy(.....)..OrderBy(g => g.Total);
int SecondNumber = result.result.ElementAtOrDefault(1).Number;
But again be aware of NRE.
Check these Methods:
.FirstOrDefault(); //gets the first element
.Take(n); //gets the first n elements
.Skip(n).FirstOrDefault(); //gets the (n+1)th element
.Skip(n).Take(m); //gets the first m elements after n elements
An anonymous type does not implement IEnumerable<T>, there for you cannot access it via index.
If you only want the very first object you can use First() or FirstOrDefault. If you cast it to an array, you can use an index and then have support for your properties again:
result.FirstOrDefault().Number;
result.ToArray()[1].Number;
I have an array of strings called "Cars"
I would like to get the first index of the array is either null, or the value stored is empty. This is what I got so far:
private static string[] Cars;
Cars = new string[10];
var result = Cars.Where(i => i==null || i.Length == 0).First();
But how do I get the first INDEX of such an occurrence?
For example:
Cars[0] = "Acura";
then the index should return 1 as the next available spot in the array.
You can use the Array.FindIndex method for this purpose.
Searches for an element that matches
the conditions defined by the
specified predicate, and returns the
zero-based index of the first
occurrence within the entire Array.
For example:
int index = Array.FindIndex(Cars, i => i == null || i.Length == 0);
For a more general-purpose method that works on any IEnumerable<T>, take a look at: How to get index using LINQ?.
If you want the LINQ way of doing it, here it is:
var nullOrEmptyIndices =
Cars
.Select((car, index) => new { car, index })
.Where(x => String.IsNullOrEmpty(x.car))
.Select(x => x.index);
var result = nullOrEmptyIndices.First();
Maybe not as succinct as Array.FindIndex, but it will work on any IEnumerable<> rather than only arrays. It is also composable.