How to optimise this LINQ Query - c#

I have this query
Dasha.Where(x => x[15] == 9).ForEachWithIndex((x,i) => dd[Sex[i]][(int)x[16]]++);
This query is finding that element in Dasha whose 15th index value is 9 and if yes it increments dd[Dashaindex][x[16]] value.
Here Dasha is double[100][50] and dd is double[2][10] and Sex is byte[ ] and can only have value 0 or 1. 0 for Male and 1 for Female
x[15] can only be between 0 to 9 (both inclusive). Same rule for x[16].
It is giving me right results.
I tried optimising this to
Dasha.ForEachWithIndex((x,i) =>
{
if(x[15] == 9)
dd[Sex[i]][(int)x[16]]++
});
This is giving me wrong results. Where am i doing wrong?
My ForEachWithIndex is like
static void ForEachWithIndex<T>(this IEnumerable<T> enu, Action<T, int> action)
{
int i = 0;
foreach(T item in enu)
action(item, i++);
}

This is just a partial answer (too long for a comment) in regards to
Dasha.ForEachWithIndex((x,i) => {
if(x[15] == 9)
dd[Sex[i]][(int)x[16]]++ });
This is giving me wrong results. Where am i doing wrong?
In the first case you filter the Dasha list of 100 items down to n items, then you iterate over these n items.
in the second case you iterate over all 100 items. So the index will be different, and the value you get from Sex[i] for each row will be different
e.g.
Dasha[0] != Dasha.Where(x => x[15] == 9)[0]
unless Dasha[0][15] == 9

You need to save original indexes before Where:
Dasha.Select((x,i) => new {x = x, i = i})
.Where(a => a.x[15] == 9)
.ForEach(a => dd[Sex[a.i]][(int)a.x[16]]++);

Following will give you same result as of first query.
int counter=0;
Dasha.ForEachWithIndex((x,i) =>
{
if(x[15] == 9)
{
dd[Sex[counter]][(int)x[16]]++;
counter++;
}
})

Related

Applying "where" clause (LINQ) on even rows only [C#]

I have a string having two different types of data in alternating rows (i.e. two rows make one record). I want to select only those records where length of 2nd (i.e. even row) is less than 1000.
I have tried this but it results in selecting only the eventh row and discards the odd row:
var lessthan1000Length = recordsFile.Where((src, index) => src.Length<1000 && index%2 != 0);
Sample data from recordsFile
2012-12-04 | 10:45 AM | Lahore
Added H2SO4 in the solution. Kept it in the lab temperature for 10 minutes
2012-12-04 | 10:55 AM | Lahore
Observed the pH of the solution.
2012-12-04 | 11:20 AM | Lahore
Neutralized the solution to maintain the pH in 6-8 range
Thanks for your guidance.
P.S: Kindly note that the results are required in the form of List<string> as we have to make a new dataset from it.
var odds = recordsFile.Where((str, index) => index % 2 == 0);
var evens = recordsFile.Where((str, index) => index % 2 == 1);
var records = odds.Zip(evens, (odd, even) => new { odd, even })
.Where(pair => pair.even.Length < 1000);
foreach (var record in records)
Console.WriteLine(record);
List<string> result = recordFile
.Select( (str, index) => new {str, index})
.GroupBy(x => x.index / 2, x => x.str)
.Where(g => g.Last().Length < 1000)
.Select(g => g.First() + g.Last())
.ToList();
Alexander's answer seems to work fine.
Alternatively, you can create a method to turn a sequence (with an even number of terms) into a sequence of pairs. I guess something like:
static IEnumerable<Tuple<T, T>> PairUp<T>(this IEnumerable<T> src)
{
using (var e = src.GetEnumerator)
{
while (e.MoveNext())
{
var first = e.Current;
if (!e.MoveNext())
throw new InvalidOperationException("Count of source must be even"); // OR: yield break; OR yield return Tuple.Create(first, default(T)); yield break;
var second = e.Current;
yield return Tuple.Create(first, second);
}
}
}
With that you could do recordsFile.PairUp().Where(t => t.Item2.Length < 1000) or similar.
Edit: Since you want the two "parts" concatenated as strings, that would be recordsFile.PairUp().Where(t => t.Item2.Length < 1000).Select(t => t.Item1 + t.Item2).
If you use Microsoft's Reactive Framework team's "Interactive Extensions" you get a nice extension method that can help you.
var query =
from pair in lines.Buffer(2)
where pair[1].Length < 1000
select pair;
var results = query.ToList();
From your sample data I get this:
Just NuGet "Ix-Main" to get the extension methods - there are a lot more there than just .Buffer and many of them are super useful.

stating which conditions have been matched in linq where

I have a linq statement as such
dbContext.Items
.Where(
p =>
(p.Client.Contact != null && p.Client.Contact.Firstname.ToLower().Contains(searchText.ToLower()))
||
(p.Client.Contact != null && p.Client.Contact.Surname.ToLower().Contains(searchText.ToLower()))
||
(p.PolicyNumber != null && p.PolicyNumber.ToLower().Contains(searchText.ToLower()))
||
(
p.PolicyLivesAssureds
.Where(
pl =>
pl.Contact != null && pl.Contact.Firstname.ToLower().Contains(searchText.ToLower())
|| pl.Contact.Surname.ToLower().Contains(searchText.ToLower())
).Count() > 0
)
)
).OrderBy(p => p.IeUtem);
This is actually needed in an autocomplete. What I want to do is being able to know exactly which among my 5 conditions has been matched and display the particular item that has been matched. For example say that PolicyNumber has been matched i want to send only policynumber for that row and for others if name has been matched i want to send only the name for that row.
Is there a way to do this;
This is a bit more of a food for thought answer as it has flaws in it's approach, but I think it does solve your problem:
double[] items = { 1, 2, 3, 4, 5 };
IEnumerable<Tuple<double, int>> results = items.Select(x =>
{
int index = 0;
foreach (var condition in new Func<bool>[]
{
// TODO: Write conditions here.
() => x == 1,
() => x == 2
})
{
if (condition() == true)
return index;
else
index++;
}
return -1;
}).Zip(items, (matchedCondtion, item) => Tuple.Create(item, matchedCondtion))
.Where(x => x.Item2 != -1);
I've used a simple double array as an example of the collection to filter, but it's just an example, you can use anything.
The first select returns an integer for each element in the collection. If there is a condition match, it returns the index of the condition. If there is not match it returns -1.
It does this by enumerating over the Func collection and returning the index of the first true condition (emulating the short circuiting of the || operator). If no conditions match it simply returns -1 after evaluating all conditions.
These results are then zipped back up with the original collection (using a Tuple), mapping each element with the index of its matching condition (or -1).
So the example would return:
{ 1, 0 },
{ 2, 1 },
{ 3, -1 },
{ 4, -1 },
{ 5, -1 }
This result is then simply filtered using Where to remove any entries with -1, leaving you with a collection of elements that matched a condition and the index of the condition that matched (in the form of a Tuple).
So to customize this for your solution, you can remove the example conditions and place whatever number of conditions you want at:
// TODO: Write conditions here.
The question becomes how do you want to know which queries match. For example you could do something like this
class AutoCompleteItem {
String Text {get; set;}
Item Item {get; set;}
}
var firstNames = dbContext.Items.Select(p => new AutoCompleteItem { Name = p.Client.Contract.FirstName, Item = p})
var lastNames = dbContext.Items.Select(p => new AutoCompleteItem { Name = p.Client.Contract.SurName, Item = p})
var result = firstName.Union(lastNames).Where(p => p.Name.Contains(searchText)).OrderBy(a => a.Item.IeUtem);
Now AutcompleteItem is a class that contains the text you want (and possibly any other fields you need, like information which field it was that matched)
The Idea here is the MVVM patttern. You have your model (the items). Now you need to construct a viewModel (AutoCompleteItems) that actual aids you in displaying what you want.

correct way of looping through a list and remove items

I wrote a function to go through a list and remove list items if some conditions where met. My program crashed on it, and after a while i concluded that the outer for loop, goes through all items in the list.
While at the same routine the list of item can get shorter.
// Lijst is a list of a struct that contains a value .scanned and .price
for (int i = 0; i < Lijst.Count; i++)
{
if (Lijst[i].scanned == false)
{
// (removed deletion of list item i here)
if (Lijst[i].price > (int)nudMinimum.Value)
{
Totaal++;
lblDebug.Text = Totaal.ToString();
}
Lijst.RemoveAt(i); //<-moved to here
}
}
Now i wonder whats the correct to do this, without getting index out of range errors.
Why not direct List<T>.RemoveAll()?
https://msdn.microsoft.com/en-us/library/wdka673a(v=vs.110).aspx
In your case
Lijst.RemoveAll(item => some condition);
E.g.
// Count all the not scanned items each of them exceeds nudMinimum.Value
lblDebug.Text = Lijst
.Where(item => !item.scanned && item.price > (int)nudMinimum.Value)
.Count()
.ToString();
// Remove all not scanned items
Lijst.RemoveAll(item => !item.scanned);
You might be looking for this
for (int i = Lijst.Count - 1 ; i >= 0 ; i--)
{
if (Lijst[i].scanned == false)
{
if (Lijst[i].price > (int)nudMinimum.Value)
{
Totaal++;
lblDebug.Text = Totaal.ToString();
}
Lijst.RemoveAt(i);
}
}
Question in the comment:
why would the other direction for loop work ?
Because when the loop is run in from Zero to Count There is a situation arise when the index is not available to remove and the count is still left. For example:
if you have 10 items in the List the loop starts at 0 and would remove 0,1,2,3,4 and now the item left are 5 and index is also 5 it would remove that item too. After that when loop value reaches 6 and item left is 4. Then it would create a problem. and it would throw an error. i.e. index out of range
here you go
// 1. Count items
lblDebug.Text = Lijst.Count(x => x.price > (int)nudMinimum.Value && !x.scanned).ToString();
//2. Remove items
Lijst.RemoveAll(x => !x.scanned);
The problems is that when you remove the element number 5, the list gets shorter and the element number 6 is now 5th, number 7 becomes 6th etc. However, if you run the loop backwards, the number is kept as expected.
for(int i = donkeys.Count - 1; i >= 0; i++)
if(donkeys[i] == some condition here)
donkeys.RemoveAt(i);
However, it's an like-a-boss approach. There are better ways. You've got the answer but I'd like to suggest a LINQ based approach.
int Totaal = Lijst
.Where(item => item.scanned)
.Where(item => item.price > (int)nudMinimum.Value)
.Count();
Lijst = Lijst.Where(item => !item.scanned).ToList()
Also, as a side note, I wonder if you find the below more readable. Consider the following different naming (both regarding the language and the capitalization).
List<Item> items = ...;
int minimum = (int)nudMinimum.Value;
int total = items
.Where(item => item.scanned)
.Where(item => item.price > minimum)
.Count();
items = items
.Where(item => !item.scanned)
.ToList();
First You are removing the element with index i and then using it. You need to first do your process with element having index i and then remove it. Your code will look like below:
for (int i = 0; i < Lijst.Count; i++)
{
if (Lijst[i].scanned == false)
{
if (Lijst[i].price > (int)nudMinimum.Value)
{
Totaal++;
lblDebug.Text = Totaal.ToString();
}
Lijst.RemoveAt(i);
}
}
Normally if you want to remove from a list all items that match a predicate, you'd use List<T>.RemoveAll(), for example:
List<int> test = Enumerable.Range(0, 10).ToList();
test.RemoveAll(value => value%2 == 0); // Remove all even numbers.
Console.WriteLine(string.Join(", ", test));
However, it seems you need to do some additional processing. You have two choices:
Do it in two steps; first use RemoveAll() to remove unwanted items, then loop over the list to process the remaining items separately.
Loop backwards from List.Count-1 to 0 instead.
your code is some how is not in proper format.
first you deleted the list item and then you are trying to catch the price of that deleted item.
How can it possible.
so you can write in this way.
for (int i = 0; i < Lijst.Count; i++)
{
if (Lijst[i].scanned == false)
{
if (Lijst[i].price > (int)nudMinimum.Value)
{
Totaal++;
lblDebug.Text = Totaal.ToString();
}
Lijst.RemoveAt(i);
}
}
List<string> list = new List<string>();
list.Add("sasa");
list.Add("sames");
list.Add("samu");
list.Add("james");
for (int i = list.Count - 1; i >= 0; i--)
{
list.RemoveAt(i);
}
How to Delete Items from List

Remove value from a Dictionary

I have a dictionary:
public ConcurrentDictionary<string, List<IronportServerStatus>> IronportServerStatusDict = new ConcurrentDictionary<string, List<IronportServerStatus>>();
In the key there are a lot of hosts (string). Lets take for an example this host abc.def.ghi. In the value there is a list of IronportServerStatus.
The next chase is that I want to remove the the first element of the value [0]. If count >= 10.
After that I want to add the new status which I get all 5 minutes. So the maximum count is 10.
I'm getting a List<List<IronportServerStatus>> and removing the first item.
After that I'm adding my new logdata which is a IronportServerStatus, this doesn't work also.
var result = SystemCore.Instance.IronportServerStatusDict.Where(o => o.Key == host.Host).Select(o => o.Value).ToList();
if (result.Count >= 10)
{
result.RemoveAt(0);
}
result.Add(logData);
Can someone help me please?
ANSWER :
SystemCore.Instance.IronportServerStatusDict.Where(o => o.Key == host.Host).FirstOrDefault().Value.RemoveAt(0);
SystemCore.Instance.IronportServerStatusDict.Where(o => o.Key == host.Host).FirstOrDefault().Value.Add(logData);
Try the following
// Get the list
var result = SystemCore.Instance.IronportServerStatusDict[host.Host];
if (result.Count >= 10)
{
result.RemoveAt(0);
}
result.Add(logData);
Console.WriteLine(SystemCore.Instance.IronportServerStatusDict[host.Host][0]);

How do I use LINQ to find 5 elements in a row that match one predicate, but where the sixth element doesn't?

I'm trying to learn LINQ and it seems that finding a series of 'n' elements that match a predicate should be possible but I can't seem to figure out how to approach the problem.
My solution actually needs a second, different predicate to test the 'end' of the sequence but finding the first element that doesn't past a test, after a sequence of at least 5 elements that do pass the test would also be interesting.
Here is my naive non-LINQ approach....
int numPassed = 0;
for (int i = 0; i < array.Count - 1; i++ )
{
if (FirstTest(array[i]))
{
numPassed++;
}
else
{
numPassed = 0;
}
if ((numPassed > 5) && SecondTest(array[i + 1]))
{
foundindex = i;
break;
}
}
A performant LINQ solution is possible but frankly quite ugly. The idea is to isolate subsequences that match the description (a series of N items matching a predicate that ends when an item is found that matches a second predicate) and then select the first of these that has a minimum length.
Let's say that the parameters are:
var data = new[] { 0, 1, 1, 1, 0, 0, 2, 2, 2, 2, 2 };
Func<int, bool> acceptPredicate = i => i != 0;
// The reverse of acceptPredicate, but could be otherwise
Func<int, bool> rejectPredicate = i => i == 0;
Isolating subsequences is possible with GroupBy and a bunch of ugly stateful code (here's the inherent awkwardness -- you have to keep non-trivial state). The idea is to group by an artificial and arbitrary "group number", choosing a different number whenever we move from a subsequence that might be acceptable to one that definitely is not acceptable and when the reverse happens as well:
var acceptMode = false;
var groupCount = 0;
var groups = data.GroupBy(i => {
if (acceptMode && rejectPredicate(i)) {
acceptMode = false;
++groupCount;
}
else if (!acceptMode && acceptPredicate(i)) {
acceptMode = true;
++groupCount;
}
return groupCount;
});
The last step (finding the first group of acceptable length) is easy, but there is one last pitfall: making sure that you don't select one of the groups that do not satisfy the stated condition:
var result = groups.Where(g => !rejectPredicate(g.First()))
.FirstOrDefault(g => g.Count() >= 5);
All of the above is achieved with a single pass over the source sequence.
Note that this code will accept a sequence of items that also ends the source sequence (i.e. it does not terminate because we found an item that satisfies rejectPredicate but because we ran out of data). If you don't want this a slight modification will be required.
See it in action.
Not elegant, but this will work:
var indexList = array
.Select((x, i) => new
{ Item = x, Index = i })
.Where(item =>
item.Index + 5 < array.Length &&
FirstTest(array[item.Index]) &&
FirstTest(array[item.Index+1]) &&
FirstTest(array[item.Index+2]) &&
FirstTest(array[item.Index+3]) &&
FirstTest(array[item.Index+4]) &&
SecondTest(array[item.Index+5]))
.Select(item => item.Index);
Instead of trying to combine existing extension methods, it is much more cleaner to use an Enumerator.
Example:
IEnumerable<T> MatchThis<T>(IEnumerable<T> source,
Func<T, bool> first_predicate,
Int32 times_match,
Func<T, bool> second_predicate)
{
var found = new List<T>();
using (var en = source.GetEnumerator())
{
while(en.MoveNext() && found.Count < times_match)
if (first_predicate((T)en.Current))
found.Add((T)en.Current);
else
found.Clear();
if (found.Count < times_match && !en.MoveNext() || !second_predicate((T)en.Current))
return Enumerable.Empty<T>();
found.Add((T)en.Current);
return found;
}
}
Usage:
var valid_seq = new Int32[] {800, 3423, 423423, 1, 2, 3, 4, 5, 200, 433, 32};
var result = MatchThis(valid_seq, e => e<100, 5, e => e>100);
Result:
var result = array.GetSixth(FirstTest).FirstOrDefault(SecondTest);
internal static class MyExtensions
{
internal static IEnumerable<T> GetSixth<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
var counter=0;
foreach (var item in source)
{
if (counter==5) yield return item;
counter = predicate(item) ? counter + 1 : 0;
}
}
}
It looks like you want continuous 6 elements, the first 5 of which match predicate1, and the last (the 6th) matches predicate2. Your non-linq version works fine, using linq in this case is a little reluctant. And trying to resolve the problem in one linq query makes the issue harder, here is a (maybe) cleaner linq solution:
int continuous = 5;
var temp = array.Select(n => FirstTest(n) ? 1 : 0);
var result = array.Where((n, index) =>
index >= continuous
&& SecondTest(n)
&& temp.Skip(index - continuous).Take(continuous).Sum() == continuous)
.FirstOrDefault();
Things will be easier if you have Morelinq.Batch method.
Like others have mentioned, LINQ is not the ideal solution to this kind of pattern matching need. But still, it is possible, and it doesn't have to be ugly:
Func<int, bool> isBody = n => n == 8;
Func<int, bool> isEnd = n => n == 2;
var requiredBodyLength = 5;
// Index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
int[] sequence = { 6, 8, 8, 9, 2, 1, 8, 8, 8, 8, 8, 8, 8, 2, 5 };
// ^-----------^ ^
// Body End
// First we stick an index on each element, since that's the desired output.
var indexedSequence = sequence.Select((n, i) => new { Index = i, Number = n }).ToArray();
// Scroll to the right to see comments
var patternMatchIndexes = indexedSequence
.Select(x => indexedSequence.Skip(x.Index).TakeWhile(x2 => isBody(x2.Number))) // Skip all the elements we already processed and try to match the body
.Where(body => body.Count() == requiredBodyLength) // Filter out any body sequences of incorrect length
.Select(body => new { BodyIndex = body.First().Index, EndIndex = body.Last().Index + 1 }) // Prepare the index of the first body element and the index of the end element
.Where(x => x.EndIndex < sequence.Length && isEnd(sequence[x.EndIndex])) // Make sure there is at least one element after the body and that it's an end element
.Select(x => x.BodyIndex) // There may be more than one matching pattern, get all their indices
.ToArray();
//patternMatchIndexes.Dump(); // Uncomment in LINQPad to see results
Note that this implementation is not performant at all, it is only meant as a teaching aid to show how something can be done in LINQ despite the unsuitability of solving it that way.

Categories