I'm trying to find the index of the first element of an ArrayList whose 'tag' property does not equal null.
I thought I could do something to the effect of
ArrayList.IndexOf(p => p.tag != null);
or
ArrayList.IndexOf(*.tag != null);
but neither of these work. Is there a way to use IndexOf with just a property of an object?
Try Array.FindIndex:
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.
If you switch to using a generic List instead of ArrayList, you can do this:
int index = list.FindIndex(p => p.tag != null);
Otherwise, you're stuck having to manually (gasp) loop through the array list.
The problem with IndexOf is that you have to use as a parameter an Object in the collection. It is not a LINQ extension. That is why it fails because your lambda is not in the collection.
You could also use the following LINQ query
ArrayList.IndexOf(ArrayList.First( x => p.tag != null ))
But regarding the performance it'll be a bit poor (looking through the collection twice)
You should rather refactor your code as suggested by the smart answers around mine.
If you are wanting to do this while still using the ArrayList you can simply call ToArray() then do your FindIndex
if you know the type of the objects you could do something like this
ArrayList list = ...;
var elem = list.Cast<T>().Select((item,i) => new {item,Index = i}).FirstOrDefault(p => p.item.tag == null);
var index = elem != null ? elem.Index : -1;
will work if you know there's at least one (not that effecient mind you
Cast turns an IEnumerable into a IEnumerable<T> opening up the door to the rest of LINQ
int index = a.Cast<T>()
.Select((p, q) => p != null && p.tag != null ? q + 1 : 0)
.FirstOrDefault(p => p > 0) - 1;
Returns -1 if no element found.
Related
I have basically achieved my desired effect through a not so elegant foreach loop. I am posting here for two reasons. One is if someone can show me a "cool" kid way to do it and or comment on reality as sometimes a foreach over an array is faster then casting to a List then using Lambda expressions.
So I am working with ExtendedAttributes property on the Artifact class.
https://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.artifact(v=vs.110).aspx
//I get my array of artifacts just fine
LinkFilter linkFilter = new LinkFilter();
linkFilter.FilterType = FilterType.ToolType;
linkFilter.FilterValues = new String[1] { ToolNames.WorkItemTracking }; //only work itms
Artifact[] artifacts = linkingService.GetReferencingArtifacts(changesetArtifactUris.ToArray(), new LinkFilter[1] { linkFilter });
//now I want to keep work items that are resolved or closed
//so I cast put into a List<T> just to then use Lambda in a for each loop
//THIS SECTION PSEUDO CODE FOR BREVITY yes I know you can't modify object you are looping over
var lst_artifacts = new List<Artifact>(artifacts);
foreach (var item in lst_artifacts)
{
lst_artifacts.RemoveAt(item.ExtendedAttributes.ElementAt(y => y.Value != "Resolved" || y.Value != "Closed"));
}
Thoughts?
Disclaimer: Not a cool kid.
What about using .Where() with a invert of your existing predicate:
authorsList = authorsList.Where(x => x.ExtendedAttributes.ElementAt(y => y.Value == "Resolved" || y.Value == "Closed")).ToList();
EDIT: Added .ToList() as I always forget it, thanks to #KyleJV
It seems that you are struggling on which function to use. You should use Any() instead of ElementAt(), your lambda y => y.Value != "Resolved" || y.Value != "Closed" is not a number, you cannot pass it into ElementAt(). You can use ElementAt(1), ElementAt(2) to get the element at specific index, but not ElementAt(true).
lst_artifacts.RemoveAll(item => !item.ExtendedAttributes.Any(y => y.Value == "Resolved" || y.Value == "Closed"));
It will remove all the items which do not have any resolved or closed attributes.
In C#, I can use something like:
List<string> myList = new List<string>();
if (myList.Count != myList.Distinct().Count())
{
// there are duplicates
}
to check for duplicate elements in a list. However, when there are null items in list this produces a false positive. I can do this using some sluggish code but is there a way to check for duplicates in a list while disregarding null values with a concise way ?
If you're worried about performance, the following code will stop as soon as it finds the first duplicate item - all the other solutions so far require the whole input to be iterated at least once.
var hashset = new HashSet<string>();
if (myList.Where(s => s != null).Any(s => !hashset.Add(s)))
{
// there are duplicates
}
hashset.Add returns false if the item already exists in the set, and Any returns true as soon as the first true value occurs, so this will only search the input as far as the first duplicate.
I'd do this differently:
Given Linq statements will be evaluated lazily, the .Any will short-circuit - meaning you don't have to iterate & count the entire list, if there are duplicates - and as such, should be more efficient.
var dupes = myList
.Where(item => item != null)
.GroupBy(item => item)
.Any(g => g.Count() > 1);
if(dupes)
{
//there are duplicates
}
EDIT: http://pastebin.com/b9reVaJu Some Linqpad benchmarking that seems to conclude GroupBy with Count() is faster
EDIT 2: Rawling's answer below seems at least 5x faster than this approach!
var nonNulls = myList.Where(x => x != null)
if (nonNulls.Count() != nonNulls.Distinct().Count())
{
// there are duplicates
}
Well, two nulls are duplicates, aren't they?
Anyway, compare the list without nulls:
var denullified = myList.Where(l => l != null);
if(denullified.Count() != denullified.Distinct().Count()) ...
EDIT my first attempt sucks because it is not deferred.
instead,
var duplicates = myList
.Where(item => item != null)
.GroupBy(item => item)
.Any(g => g.Skip(1).Any());
poorer implementation deleted.
I have an array of (always 4) objects, which I need to order by descending value of an object member.
I had thought to order it as
Array = Array.OrderByDescending(p => p.Val)
This fell over when one of the values was null, of course. So what I am aiming for, but my LINQ is not up to, is:
Array = Array.OrderByDescending(p => if( p != null ) p.Val; else float.MinValue)
How can I accomplish this ordering without having to delete and later re-add the null value? Thanks for your help.
Use the ternary conditional operator:
Array = Array.OrderByDescending(p => p != null ? p.Val : float.MinValue)
Per the comments below, the reason you can't use the if/else is because the body of the lambda (the stuff to the right of p =>) must be an expression, unless you surround the whole thing with curly braces. So to illustrate, you could also use the if/else if you wanted:
Array = Array.OrderByDescending(p =>
{
if (p != null) return p.Val;
else return float.MinValue;
});
But clearly more verbose.
I'm not sure what objects/types you're working with, but perhaps try a ternary operator like the following:
Array = Array.OrderByDescending(p => p == null ? float.MinValue : p.Val)
Use this operator:
Array = Array.OrderByDescending( p => p ?? float.MinValue)
Is this code good enough:
if (MyCollection.Where(p => p.SomeID == idstring).Any())
{
selectedval = MyCollection.Where(p => p.SomeID == idstring).FirstOrDefault().MyField;
}
My doubt is about the fact that I make the same query twice: first for null check and then to actually get data.
Maybe there is a better way to do this type of things?
Yes.
var item = MyCollection.FirstOrDefault(p => p.SomeID == idstring);
if (item != null)
selectval = item.MyField;
This avoids double querying the collection, which will certainly make a difference in big collections or if your collection performs a DB query.
There is. You can use the FirstOrDefault method which takes a predicate and returns null if the item is not found.
var result = MyCollection.FirstOrDefault(p => p.SomeID == idstring);
if( result != null )
{
// item was found
}
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.