I have an array of doubles and a threshold value.
I would like to select the first index in my array where the value at the index is larger than the threshold.
How the do I accomplish that in LINQ?
I got it to work with:
var n = acc_avg.Select((val, index) => new {Val = val, Index = index})
.Where(l => l.Val > threshold)
.First()
.Index
But is there is better way?
You can use Array.FindIndex:
var n = Array.FindIndex(acc_avg, x => x > threshold);
Your solution looks pretty decent to me, but I believe it will throw an exception if there are no elements in the sequence that meet your criteria. I'd consider FirstOrDefault instead of First and test for null before accessing.
var n = acc_avg.Select((val,index) => new {Val= val, Index = index}).Where(l=> l.Val > threshold).FirstOrDefault();
if(n != null)
DoSomething(n.Index);
Of course, if your object already had an index property (or if the location in the sequence isn't important to you) you could shorten this to:
var n = acc_avg.FirstOrDefault(l => l > threshold);
But you probably knew that. :)
Related
This question already has answers here:
How to get index using LINQ? [duplicate]
(7 answers)
Closed 2 years ago.
I am starting in C# Linq. I would like to performance this query.
I Got an List of object List with several values per object, one of them is a "Text". I would like to seek which object have an especific text them get the index of the Object for the List.
to get index is not completed
var List = GetListObject();
var index = unitList.Select(x => x.Text.ToString().Contains("Text"));
check this
myLÄ°st.Select((v, i) => new {Text = v, Index = i})
.First(x=> x.Text.ToString().Contains("Text")).Index;
If you want to obtain index, I suggest good old for loop:
int index = -1;
for (int i = 0; i < List.Count; ++i)
if (List[i].Text.Contains("Text")) {
index = i;
break;
}
In case of Linq
int index = List
.Select((v, i) => new {v, i})
.FirstOrDefault(pair => pair.v.Text.Contains("Text"))
?.i ?? -1;
In both cases we get -1 if item with Text == "Text" is not found
You can use Enumerable.Range(int, int).
For example: var zeroIndexes = Enumerable.Range(0, list.Count - 1).Where(x => list[x] == 0).ToArray() will give you an array of all indexes of list where the value is equal to zero.
So specific to your question, you can do:
int index = Enumerable.Range(0, unitList.Count - 1).Where(x => unitList[x].Text.ToString().Contains("Text")).FirstOrDefault();
This will give your the first index or 0 if not found.
I want to create a loop to check a list of titles for duplicates.
I currently have this:
var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));
foreach (var x in productTitles)
{
var title = x.Text;
productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));
foreach (var y in productTitles.Skip(productTitles.IndexOf(x) + 1))
{
if (title == y.Text)
{
Assert.Fail("Found duplicate product in the table");
}
}
}
But this is taken the item I skip out of the array for the next loop so item 2 never checks it's the same as item 1, it moves straight to item 3.
I was under the impression that skip just passed over the index you pass in rather than removing it from the list.
You can use GroupBy:
var anyDuplicates = SeleniumContext
.Driver
.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
.GroupBy(p => p.Text, p => p)
.Any(g => g.Count() > 1);
Assert.That(anyDuplicates, Is.False);
or Distinct:
var productTitles = SeleniumContext
.Driver
.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
.Select(p => p.Text)
.ToArray();
var distinctProductTitles = productTitles.Distinct().ToArray();
Assert.AreEqual(productTitles.Length, distinctProductTitles.Length);
Or, if it is enough to find a first duplicate without counting all of them it's better to use a HashSet<T>:
var titles = new HashSet<string>();
foreach (var title in SeleniumContext
.Driver
.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
.Select(p => p.Text))
{
if (!titles.Add(title))
{
Assert.Fail("Found duplicate product in the table");
}
}
All approaches are better in terms of computational complexity (O(n)) than what you propose (O(n2)).
You don't need a loop. Simply use the Where() function to find all same titles, and if there is more than one, then they're duplicates:
var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));
foreach(var x in productTitles) {
if (productTitles.Where(y => x.Text == y.Text).Count() > 1) {
Assert.Fail("Found duplicate product in the table");
}
}
I would try a slightly different way since you only need to check for duplicates in a one-dimensional array.
You only have to check the previous element with the next element within the array/collection so using Linq to iterate through all of the items seems a bit unnecessary.
Here's a piece of code to better understand:
var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
for ( int i = 0; i < productionTitles.Length; i++ )
{
var currentObject = productionTitles[i];
for ( int j = i + 1; j < productionTitles.Length; j++ )
{
if ( currentObject.Title == productionTitles[j].Title )
{
// here's your duplicate
}
}
}
Since you've checked that item at index 0 is not the same as item placed at index 3 there's no need to check that again when you're at index 3. The items will remain the same.
The Skip(IEnumerable, n) method returns an IEnumerable that doesn't "contain" the n first element of the IEnumerable it's called on.
Also I don't know what sort of behaviour could arise from this, but I wouldn't assign a new IEnumerable to the variable over which the foreach is being executed.
Here's another possible solution with LINQ:
int i = 0;
foreach (var x in productTitles)
{
var possibleDuplicate = productTitles.Skip(i++).Find((y) => y.title == x.title);
//if possibleDuplicate is not default value of type
//do stuff here
}
This goes without saying, but the best solution for you will depend on what you are trying to do. Also, I think the Skip method call is more trouble than it's worth, as I'm pretty sure it will most certainly make the search less eficient.
I'm hitting a brick wall with this, and I just can't seem to wrap my head around it.
Given a List of objects, how can i get every third element starting from the end (so the third to last, sixth to last etc) but if it gets to the end and there are only 1 or 2 left, returns the first element.
I'm essentially trying to simulate drawing three cards from the Stock and checking for valid moves in a game of patience, but for some reason i'm struggling with this one concept.
EDIT:
So far I've tried looked into using the standard for loop increasing the step. That leads me to the second need which is to get the first element if there are less than three on the final loop.
I've tried other suggestions on stack overflow for getting nth element from a list, however they all also don't provide the second requirement.
Not entirely sure what code i could post that wouldn't be a simple for loop. as my problem is the logic for the code, not the code itself.
For example:
Given the list
1,2,3,4,5,6,7,8,9,10
i would like to get a list with
8, 5, 2, 1
as the return.
pseudocode:
List<object> filtered = new List<object>();
List<object> reversedList = myList.Reverse();
if(reversedList.Count % 3 != 0)
{
return reversedList.Last();
}
else
{
for(int i = 3; i < reversedList.Count; i = i +3)
{
filterList.Add(reversedList[i]);
}
if(!filterList.Contains(reversedList.Last())
{
filterList.Add(reversedList.Last());
}
Try using this code -
List<int> list = new List<int>();
List<int> resultList = new List<int>();
int count = 1;
for (;count<=20;count++) {
list.Add(count);
}
for (count=list.Count-3;count>=0;count-=3)
{
Debug.Log(list[count]);
resultList.Add(list[count]);
}
if(list.Count % 3 > 0)
{
Debug.Log(list[0]);
resultList.Add(list[0]);
}
Had to try and do it with linq.
Not sure if it live up to your requirements but works with your example.
var list = Enumerable.Range(1, 10).ToList();
//Start with reversing the order.
var result = list.OrderByDescending(x => x)
//Run a select overload with index so we can use position
.Select((number, index) => new { number, index })
//Only include items that are in the right intervals OR is the last item
.Where(x => ((x.index + 1) % 3 == 0) || x.index == list.Count() - 1)
//Select only the number to get rid of the index.
.Select(x => x.number)
.ToList();
Assert.AreEqual(8, result[0]);
Assert.AreEqual(5, result[1]);
Assert.AreEqual(2, result[2]);
Assert.AreEqual(1, result[3]);
Is there a way to check that at least one element appears more than once in an array are equal without sequentially comparing every element.
Example: In int[] array1 = { 1, 3, 4, 2, 4 }; the element 4 appears twice.
You just need to check if there is any difference between original array and it's Distinct() version.
var result = (array1.Count()-array1.Distinct().Count())>0;
You can use Distinct() method to get unique values of array and compare length with original array
int[] arrayDistinctElements = array1.Distinct().ToArray();
if(arrayDistinctElements.length == array1.length)
{
//All unique elements
}
else
{
//Duplicates were present
}
This might do the trick for you
var duplicates = array1.GroupBy(p => p).Where(g => g.Count() > 1).Select(g => g.Key);
variable duplicates contains the list of repeated items
EDIT
If you want to the return value to be Boolean than
var duplicates = array1.GroupBy(p => p).Where(g => g.Count() > 1).Select(g => g.Key).Count() > 0;
You can also do something like this
if(array1.Distinct().Count() != array1.Count())
return true; /// You have duplicates in the array
else
return false; /// All the elements in the array are different
You can use the following code:
var isExisted = list.Count(item => item.Param == "test") >= 2;
Sample solution in your case: https://dotnetfiddle.net/1y4w9K
I am discovering the tools C# provides to work with collections.
Suppose I have a List of elements, and I want to retrieve the one that most satisfies a property. Basically a elements.Max(predicate), except that I am interested in the index of the best element. The reason I want the index and not the element itself is there might not be such element, and the type is non-nullable.
Writing a function doing this is trivial, but I am interested in using the expressiveness of the tools C# provides to get a solution that is both concise, clear, and optimal (O(n)).
At this point I have the following code, which still looks cluttered, and evaluates the property twice.
List<foo> elements;
private int getBest(object x)
{
var indices = Enumerable.Range(0, elements.Count);
return indices.Aggregate(-1, (best, next) =>
(-1 == best || eval(x, elements[next]) > eval(x, elements[best])) ? next : best);
}
How can I make this piece of code better?
Addendum: I didn't put it in the code for the sake of clarity, but if eval() is below a certain threshold the element is discarded.
I'd recommend using the Select in conjunction with Aggregate LINQ extension methods. Using the Select method you can create an anonymous type which houses the index and value of each item within your collection. Then using the LINQ Aggregate method, you can narrow down the item with the greatest value. Some like this should work I think:
private int GetIndexOfHighestValue(IEnumerable<int> list)
{
return list.Select((i, v) => new { Index = i, Value = v })
.Aggregate((a, b) => (a.Value > b.Value) ? a : b)
.Index;
}
Doing it with LINQ is fun, but doing it without LINQ makes it more intuitive:
int bestIndex = -1;
int bestResult = -1;
for(int i = 0; i < elements.Count; ++i)
{
int currentResult = eval(x, elements[i]);
if (currentResult > bestResult)
{
bestResult = currentResult;
bestIndex = i;
}
}
Something like this could work:
// OOPS: This won't work because Max is defined the way it is. Always bugged me...
var result = elements.Select((e, i) => new {Element = e, Index = i}).Max(x => x.Element).Select(x => x.Index);
oh rats. Right. This will not work. So: Let's pull out our allrounder: Aggregate. Here we go:
var elements = new List<int>{1, 7, 2, 5};
var result = elements.Select((e, i) => new {Element = e, Index = i})
.Aggregate(
new { Element = elements.First(), Index = -1}, // gotta start somewhere and Element is non-nullable according to OP
(max, next) => (max.Element > next.Element) && max.Index >= 0 ? max : next,
max => max.Index);
This results in 1. Did that help?