Determine if an int value exists once in an array - c#

I know you can use Any, Exists, and Single with LINQ but can't quite get this to work. I need to do a lookup based on an id to see if it's in the array and make sure that there is only ONE match on that value. because if there are 2 it's gonna cause an issue..the requirement that I'm checking is that the array only has one and only one of each ID in the array.
Here's what I tried
if(someIntArray.Single(item => item = 3)
//... we found the value 8 in the array only once so now we can be confident and do something

Here's how I would solve this:
if (someIntArray.Count(item => item == 3) == 1)
{
//only one '3' found in the array
...
}

I created a One() extension method set for just this situation:
public static bool One<T>(this IEnumerable<T> sequence)
{
var enumerator = sequence.GetEnumerator();
return enumerator.MoveNext() && !enumerator.MoveNext();
}
public static bool One<T>(this IEnumerable<T> sequence, Func<T, bool> predicate)
{
return sequence.Where(predicate).One();
}
//usage
if (someIntArray.One(item => item == 3)) ...
The problem with Single() is that it throws an exception if there isn't exactly one element. You can wrap it in a try-catch, but these are cleaner, and more efficient than Count() in most cases where there's more than one matching element. Unfortunately, there's no way around having to check the entire array to verify that there are either no elements or only one that matches a predicate, but this will at least "fail fast" if there are two or more, where Count() will always evaluate the entire Enumerable whether there's one matching element or fifty.

I think you're overthinking this.
var targetNumber = 3;
var hasExactlyOne = someIntArray.Count(i => i == targetNumber) == 1;

Using LINQ expression:
var duplicates = from i in new int[] { 2,3,4,4,5,5 }
group i by i into g
where g.Count() > 1
select g.Key
Results:
{4,5}
And of course you could check duplicates.Count() > 0 or log the ones that are a problem or whatever you need to do.

got it working:
if(someIntArray.Single(item => item = 3) > 0)
doh

Related

Indexing IQueryable<int>?

How do you index an IQueryable?
I am using a LINQ to sql query to get in values from a particular column. The query is as follows,
var intitalQuery = (from a in sql.GetTable<Staff_Time_TBL>()
where a.Info_Data == SelectedOption
select a.Staff_No).Distinct();
From there I want to be able index the intitalQuery variable and get values as needed.
That value is then used in another query.
My first try was this,
Column1.DataContext = sql.Staff_Time_TBLs.Where(item =>
item.Section_Data == SelectedOption &&
item.Staff_No == intitalQuery[0];
Then I tried this from here with no luck.
Column1.DataContext = sql.Staff_Time_TBLs.Where(item =>
item.Section_Data == SelectedOption &&
item.Staff_No == intitalQuery.First());
From what I can from the link is that that way gets just the first value, I want to be able to get all values via indexing. How do you go about that?
IQueryable<T> inherits from IEnumerable and as such has a wealth of extension methods to accomplish almost anything you'd need from a sequence. In particular, .ToList() turns an enumerable into a List<T> that allows efficient indexing.
.ToList() is slightly more efficient than the more obvious .ToArray() when working with sequences of unknown initial length, because .ToArray() requires an additional copy to end up with an array of exactly the right size. (But arrays are faster to loop over, so it all depends on what you're doing.)
You can do this:
public static List<Staff_Time_TBLs> GetIndexed(string staffNo){
var stuff = sql.Staff_Time_TBLs.Where(item =>
item.Section_Data == SelectedOption &&
item.Staff_No == staffNo;
return stuff.ToList();
}
//to use it...
initialQuery.ForEach(p=>{
var indexvalue = GetIndexed(p)
});

Sequence contains more than one matching element

When I am trying to set IsDefault property of each dressing items where match a condition it throws an error saying:
Sequence contains more than one matching sequence.
(this.DressingItems
.Where(xx => xx.DressingInfo.CatID == catId
&& xx.ProductID == this.ProductID)
.Single()).IsDefault = false;
Well, this exception says that at least two items of the sequence DressingItems match your Where condition. The call to Single then causes the exception because it asserts that only one item is passed in.
Reading your question makes me think that you want to do something on each item of the input sequence, so you will probably use a foreach loop:
foreach(var item in this.DressingItems.Where(xx => xx.DressingInfo.CatID == catId && xx.ProductID == this.ProductID))
{
item.IsDefault = false;
}
this.DressingItems.Where(x=> x.DressingInfo.CatID == catId &&
x.ProductID == this.ProductID).ToList()
.ForEach(item=>item.IsDefault = false);
The point of the Single operator is to assert that a given sequence only has one item. For instance when retrieving a specific instance by primary key.
I suppose you want to mutate the state of any DressingItem matching the criteria, in which case you have some options, all involving enumerating the resultset, and executing some behavior.
There is no LINQ operator to specifically do this, since LINQ operators are meant to be pure. Pure functions are functions that do not have side effects, and this is exactly what you are trying to do.
There is, however, an extensionmethod on List<T> which does allow this. e.g.
this.DressingItems.Where(di => di.DressingInfo.CatID == catId
&& di.ProductID == this.ProductID)
.ToList()
.ForEach(di =>
{
di.IsDefault = false
});
Or you could roll your own:
public static class EnumerableExtensions
{
public static IEnumerable<T> ForEach<T>(
this IEnumerable<T> source,
Action<T> mutator)
{
var buffered = source.ToList();
buffered.ForEach(mutator);
return buffered;
}
}
You might ask why the guys at Microsoft decided against adding this to the BCL: As I recall, the idea was that an extensionmethod vs. a foreach() { } construct would not yield much benefits in terms of typing anyway, and it wouldn't help at all in terms of ambiguity. All other operators are side-effect free, and this one is explicitely designed to induce them.
It is an InvalidOperationException thrown by the Single method.
The method is supposed to return only one element, please check the criteria that you use on your query.
However an exception is also thrown when it fails to find any element
you have more than one item in this.DressingItems that match the given CatId and Product Id.
if you are sure there must be one (single), then you have to review how is this.DressingItems loaded.
if it's expected there are more than one, then you have to use foreach to set the values.
Since you are looking for a one liner, you could create your own method doing that.
public static void DoActionForEachElement<T>(IEnumerable<T> items, Func<T, bool> predicate, Action<T> action)
{
foreach (var item in items)
{
if (predicate(item))
action(item);
}
}
and then call it by
DoActionForEachElement(
DressingItems,
xx => xx.DressingInfo.CatID == catId && xx.ProductID == ProductID,
x => x.IsDefault = false);
This way you don't have to cast the result from Where to a List first.

Search in part of the list by LINQ

I want to know if there's a way to search from a specific index in LINQ
This is what i want to do for example :
//search after the 4th element if exist any true
bool result = myList.Any(a=>a.flagTodo)
// in my case I want to do like: myList.where(a=>a.index >4).Any(a=>a.flagTodo)
there's any way to do it in the same linq ?
You can use Enumerable.Skip to bypass specified number of elements in sequence:
bool result = myList.Skip(4).Any(a => a.flagTodo);
BTW you don't need to verify list length - if there is less than 4 items, result will be false - Skip(4) will return empty sequence. And Any returns false because none of elements in sequence satisfies condition (sequence is empty).
You can skip the first 4 elements.
bool result = myList.Skip(4).Any(a => a.flagTodo);
But you need to check on your list length first before calling this statement.
Either use Skip as others have mentioned or pass the index so that you can use it in Any
bool result = myList.Select((obj, index) => new { obj, index })
.Any(x => x.index > 4 && x.obj.flagTodo);

LINQ to find array indexes of a value

Assuming I have the following string array:
string[] str = new string[] {"max", "min", "avg", "max", "avg", "min"}
Is it possbile to use LINQ to get a list of indexes that match one string?
As an example, I would like to search for the string "avg" and get a list containing
2, 4
meaning that "avg" can be found at str[2] and str[4].
.Select has a seldom-used overload that produces an index. You can use it like this:
str.Select((s, i) => new {i, s})
.Where(t => t.s == "avg")
.Select(t => t.i)
.ToList()
The result will be a list containing 2 and 4.
Documentation here
You can do it like this:
str.Select((v,i) => new {Index = i, Value = v}) // Pair up values and indexes
.Where(p => p.Value == "avg") // Do the filtering
.Select(p => p.Index); // Keep the index and drop the value
The key step is using the overload of Select that supplies the current index to your functor.
You can use the overload of Enumerable.Select that passes the index and then use Enumerable.Where on an anonymous type:
List<int> result = str.Select((s, index) => new { s, index })
.Where(x => x.s== "avg")
.Select(x => x.index)
.ToList();
If you just want to find the first/last index, you have also the builtin methods List.IndexOf and List.LastIndexOf:
int firstIndex = str.IndexOf("avg");
int lastIndex = str.LastIndexOf("avg");
(or you can use this overload that take a start index to specify the start position)
First off, your code doesn't actually iterate over the list twice, it only iterates it once.
That said, your Select is really just getting a sequence of all of the indexes; that is more easily done with Enumerable.Range:
var result = Enumerable.Range(0, str.Count)
.Where(i => str[i] == "avg")
.ToList();
Understanding why the list isn't actually iterated twice will take some getting used to. I'll try to give a basic explanation.
You should think of most of the LINQ methods, such as Select and Where as a pipeline. Each method does some tiny bit of work. In the case of Select you give it a method, and it essentially says, "Whenever someone asks me for my next item I'll first ask my input sequence for an item, then use the method I have to convert it into something else, and then give that item to whoever is using me." Where, more or less, is saying, "whenever someone asks me for an item I'll ask my input sequence for an item, if the function say it's good I'll pass it on, if not I'll keep asking for items until I get one that passes."
So when you chain them what happens is ToList asks for the first item, it goes to Where to as it for it's first item, Where goes to Select and asks it for it's first item, Select goes to the list to ask it for its first item. The list then provides it's first item. Select then transforms that item into what it needs to spit out (in this case, just the int 0) and gives it to Where. Where takes that item and runs it's function which determine's that it's true and so spits out 0 to ToList, which adds it to the list. That whole thing then happens 9 more times. This means that Select will end up asking for each item from the list exactly once, and it will feed each of its results directly to Where, which will feed the results that "pass the test" directly to ToList, which stores them in a list. All of the LINQ methods are carefully designed to only ever iterate the source sequence once (when they are iterated once).
Note that, while this seems complicated at first to you, it's actually pretty easy for the computer to do all of this. It's not actually as performance intensive as it may seem at first.
While you could use a combination of Select and Where, this is likely a good candidate for making your own function:
public static IEnumerable<int> Indexes<T>(IEnumerable<T> source, T itemToFind)
{
if (source == null)
throw new ArgumentNullException("source");
int i = 0;
foreach (T item in source)
{
if (object.Equals(itemToFind, item))
{
yield return i;
}
i++;
}
}
You need a combined select and where operator, comparing to accepted answer this will be cheaper, since won't require intermediate objects:
public static IEnumerable<TResult> SelectWhere<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, bool> filter, Func<TSource, int, TResult> selector)
{
int index = -1;
foreach (var s in source)
{
checked{ ++index; }
if (filter(s))
yield return selector(s, index);
}
}

What is the collection version of SingleOrDefault for a Dictionary<T>?

Title kind of says it all. I just can't seem to find a DictionaryOrDefault \ ListOrDefault \ CollectionOrDefault option.
Is there such a method? If not how do I do this:
MyClass myObject = MyDictionary
.SingleOrDefault(x =>
{
if (x.Value != null)
return (x.Value.Id == sourceField.SrcField.Id);
else
return false;
}).Key;
if there is more than one match? (I am getting an execption because SingleOrDefault is only meant for single results (imagine that!).)
Guess I needed to be clearer (though the where answers looks good).
I have the above statement. I changed my program so that it does not always return 1 (there can be several values that match one key). That fails so I am looking for a collection to be returned (rather than just one item).
You can use IEnumerable<T>.FirstOrDefault(Func<T, bool> predicate) if your intention is to return the first one matching the predicate.
Otherwise you're simply looking at the IEnumerable<T>.Where(Func<T, bool> predicate) linq extension, which will return all elements that match the predicate passed. That will return an empty IEnumerable<T> if no elements match the predicate, at which point if you really need the value to be null, you can just look if anything is in it.
var res = MyDictionary.Where(x =>
{
if (x.Value != null)
return (x.Value.Id == sourceField.SrcField.Id);
return false;
});
if (!res.Any())
res = null;
Then if you absolutly need to have it as a list, you can just call
res.ToList();
Note that if you're actually manipulating a Dictionary<TKey, TValue>, res will contain KeyValuePair<TKey, TValue>'s.
if you do something like
var mylist = obj.Where(x=>x.attr1 == 4);
you can then check if anything was returned using the .Any() method
mylist.Any()

Categories