How would I convert the following using LINQ?
foreach (int[] arr in jaggedArray)
{
if (arr[7] == 1)
{
if (!CheckThis(arr))
boolSuccess = false;
else
intCount++;
}
}
Something like this:
var filtered = jaggedArray.Where(arr => arr[7] == 1);
var intCount = filtered.Where(ar => CheckThis(ar)).Count()
var boolSuccess = filtered.Count() == intCount;
Nested query has to be written for the same logic or else the source code logic has to be simplified first to get a simple query
I use ReSharper, which suggests when a Linq expression is better - and performs the translation. Sometimes I keep my code though, when the Linq becomes too complex and hard to understand.
I believe this would work: First, it sets intCount by getting all of the items that satisfy arr[7]==1 and pass the CheckThis() method. That should be your intCount value.
Then, if the value in intCount doesn't match the length of the array, at least one thing failed, so you can set boolSuccess to false.
Please note that I consider this solution to be more clever, and less readable. Less readable is always a bad thing. Plus, you could refactor your existing method easily, whereas doing it this way would be much harder to refactor due to the Cleverness Factor.
intCount = jaggedArray.Where(x => x[7] == 1).Where(CheckThis).Count();
if (intCount != jaggedArray.Length) boolSuccess = false;
You can use Array.ForEach, although I think what you started with is actually clearer
Array.ForEach(jagged, arr =>
{
if (arr[7] == 1)
{
if (!CheckThis(arr))
{
boolSuccess = false;
}
else
{
intCount++;
}
}
});
intCount += jaggedArray
.Where(arr => arr[7] == 1)
.Select(arr =>
{
int val = CheckThis(arr) ? 1 : 0;
if (val == 0) {boolSuccess = false;}
return val;
}).Sum()
Related
This one should be easy, but seems to be eluding me.
Given this variable: (which contains ~30 records)
var seriesData = new List<List<object>>();
How do I loop through every record, and omit any record that contains a null, anywhere inside?
Typically, each list inside will look like one of the following:
["02/16/2019", 5, 7, 10]
["02/17/2019", 3, 15, 2]
and sometimes:
["02/18/2019", 5, {null}, 10]
This is what I have tried, but, it's not working:
foreach (List<object> row in seriesData)
{
if (row.Contains(null)) seriesData.Remove(row);
}
The result I'm ending up with is completely empty?
You can use RemoveAll which accepts predicate:
seriesData.RemoveAll(row => row.Any(x => x == null))
If you can use LINQ, this should be easy:
seriesData = seriesData
// filter the lists (x) where all items in them (y) are not null
.Where(x => x.All(y => y != null))
// and get the result
.ToList();
Without LinQ, you may do something like this:
int i = 0;
while (i < seriesData.Count)
{
if (seriesData[i].Contains(null))
{
seriesData.RemoveAt(i);
} else {
i++;
}
}
This may very well be the most performant solution and not require LinQ if you don't use it already. If, on the other hand, you already use LinQ, then style may be more important than performance.
As an exercise, I write a version that changes the order of entries but has a lower complexity. As stated by #Lee, the above code may have an O(n^2) complexity. Here is another version, maybe some benchmarking if performance is really important would help:
int i = 0, last;
while (i < seriesData.Count)
{
if (seriesData[i].Contains(null))
{
last = seriesData.Count - 1;
seriesData[i] = seriesData[last];
seriesData.RemoveAt(last);
} else {
i++;
}
}
There are many ways to skin a cat. Here is yet another one that doesn't modify your original list:
var nonulls = seriesData.Where(sd => !sd.Any(o => o == null));
I have a list of transactions and i need to find if there is more then 1 account
i did
var MultipleAccounts = list.GroupBy(t => t.AccountId).Count() > 1;
is there a better way?
If you're willing to lose the single-line I prefer the use of !.All(item => bool) or .Any(item => bool) as I think it's the most semantic and easiest to read, as well as being a good candidate for the fastest.
var accountId = accounts[0].AccountId;
var hasMultipleAccounts = !accounts.All(account => account.AccountId == accountId);
Alternatively, and perhaps even more semantically, you could use .Any(item => bool) instead of .All(item => bool).
var accountId = accounts[0].AccountId;
var hasMultipleAccounts = accounts.Any(account => account.AccountId != accountId);
Things to watch out for are making sure you have at least one item (so that accounts[0] doesn't fail) and not doing a multiple enumeration of your IEnumerable. You say you're working with a List, so multiple enumeration shouldn't cause you any trouble, but when you just have an unknown IEnumerable it's important to be careful.
I prefer:
var MultipleAccounts = list.Select(t => t.AccountId).Distinct().Skip(1).Any();
This should be exceedingly fast as it will stop iterating the source list as soon as it finds a second AccountId.
Anytime you execute a full .Count() it has to iterate the full source list.
You can test this with the following code:
void Main()
{
Console.WriteLine(Data().Select(t => t).Distinct().Skip(1).Any());
}
private Random __random = new Random();
public IEnumerable<int> Data()
{
while (true)
{
var #return = __random.Next(0, 10);
Console.WriteLine(#return);
yield return #return;
}
}
A typical run looks like this:
7
9
True
Ok here is what i found the quickest
public bool HasMultipleAccounts(List<Account> list)
{
foreach (var account in list)
if (account.AccountId != list[0].AccountId)
return true;
return false;
}
usage: var MultipleAccounts = HasMultipleAccounts(list);
Credits: #hvd
i know its more code but if you think what the cpu needs to do its the quickest
I'm using the RemoveAll() statement, that do a foreach element of the list, and based in a condition returned by the delegate, it removes or not the element from the list. Like this:
x.RemoveAll(delegate(string y)
{
if (y == "abc")
return true;
return false;
});
I want to break the foreach from the removeAll, so that upon fulfilling some condition, I no longer even try to remove elements. Something like this:
x.RemoveAll(delegate(string y)
{
if (Foo() || Bar())
break; //stop trying to remove elements
if (y == "abc")
return true;
return false;
});
Have a way to do this without a auxiliary variable?
P.S: Using a auxiliary variable I know how to do.
There are two real options. A variable telling you not to remove more items:
var done = false;
list.RemoveAll(item =>
{
if(done) return false;
if(Foo() || Bar())
{
done = true;
return false;
}
return item == "abc";
}
Or throwing an exception (despite the fact that it's really poor practice to use exceptions for control flow).
list.RemoveAll(item =>
{
if(Foo() || Bar())
throw new SomeTypeOfException()
return item == "abc";
}
If Foo or Bar being true really are exceptional/error cases, then maybe you could justify it, but it certainly seems like code smell. Note that this is technically going to be the only way to use RemoveAll and not actually invoke the delegate on any later items.
Fundamentally the problem is that the operation you're trying to perform isn't in line with what RemoveAll was designed to do. What you really want is a version of the method that supports cancellation, or sufficient access to the internals of the list to create a comparable method with the appropriate cancellation. Sadly, you don't have access to the underlying array in order to be able to replicate the ability of RemoveAll to remove multiple items without moving up all of the items until the very end, unless you re-create your own entire list based structure.
Using simple loop with break will be way more efficient then using DeleteAll with trigger to generate false for all elements after a certain point.
Why not rather filter first:
foreach(var item in x.Where(o => Foo(o)).ToList())
x.Remove(item);
If you care about efficiency, then
for(int i = 0; i++; i < x.Length)
if(Foo(x[i]))
{
x.RemoveAt(i);
break; // if list is sorted
}
else
i++;
For unsorted list it's more optimal to go from top to bottom afaik.
You could do this with an extension method:
public static IEnumerable<T> RemoveAllUntil<T>(
this IEnumerable<T> input,
Predicate<T> match,
Predicate<T> until)
{
bool untilFound = false;
foreach (T element in input)
{
if(!untilFound) untilFound = until(element);
if(untilFound || !match(element))
{
yield return element;
}
}
}
And use it like this:
var strings = new List<string> { "s1", "s2", "s2", "break", "s2", "s3"};
strings = strings.RemoveAllUntil(
s => s == "s2",
s => s == "break")
.ToList();
This will give you:
s1, break, s2, s3
Long comment: approximate code for removing item with cancellation and avoiding multiple copying of the tail:
void RemoveWithCancelation(this List<T> list,
Func<RemoveWithCancelationResult> predicate)
{
var indexToKeep = -1;
for (var i = 0; i < list.Count; i++)
{
var condition = predicate(list[i]);
if (condition.Cancel)
break;
if (!condition.RemoveItem)
{
indexToKeep++;
list[indexToKeep] = list[i];
}
}
if (indexToKeep+1 < list.Count)
list.RemoveRange(indexToKeep+1, list.Count);
}
I don't think this can be done in the RemoveAll().
However, you could 'simulate' the break with TakeWhile() and then filter the list with Where()
var newList = x.TakeWhile(elem => elem != "your condition").Where(elem => elem == "abc");
You could take advantage of closures:
bool stop = false; // the delegate closes over 'stop'
x.RemoveAll(delegate(string y)
{
if (!stop && y == "abc")
return true;
if (Foo() || Bar())
stop = true;
return false;
});
Can we use foreach loop for IQueryable object?
I'd like to do something as follow:
query = IQueryable<Myclass> = objDataContext.Myclass; // objDataContext is an object of LINQ datacontext class
int[] arr1 = new int[] { 3, 4, 5 };
foreach (int i in arr1)
{
query = query.Where(q => (q.f_id1 == i || q.f_id2 == i || q.f_id3 == i));
}
I get a wrong output as each time value of i is changed.
The problem you're facing is deferred execution, you should be able to find a lot of information on this but basically none of the code s being executed until you actually try to read data from the IQueryable (Convert it to an IEnumerable or a List or other similar operations). This means that this all happens after the foreach is finished when i is set to the final value.
If I recall correctly one thing you can do is initialize a new variable inside the for loop like this:
foreach (int i in arr1)
{
int tmp = i;
query = query.Where(q => (q.f_id1 == tmp || q.f_id2 == tmp || q.f_id3 == tmp));
}
By putting it in a new variable which is re-created each loop, the variable should not be changed before you execute the IQueryable.
You dont need a for each, try it like this:
query = objDataContext.Myclass.Where(q => (arr1.Contains(q.f_id1) || arr1.Contains(q.f_id2) || arr1.Contains(q.f_id3));
this is because "i" is not evaluated until you really use the iterate the query collectionif not by that time I believe "i" will be the last.
var sortQ = filterQ; // <--- update
if (iSortingCols > 0)
{
sortQ = (sortDirs[0] == "asc") ?
sortQ.OrderBy((d) => d.GetColumnData()[sortCols[0]]) :
sortQ.OrderByDescending((d) => d.GetColumnData()[sortCols[0]]);
if (iSortingCols > 1)
{
for (int i = 1; i < iSortingCols; i++)
{
sortQ = (sortDirs[i] == "asc") ?
sortQ.ThenBy(d => d.GetColumnData()[sortCols[i]]) :
sortQ.ThenByDescending(d => d.GetColumnData()[sortCols[i]]);
}
}
}
The compiler underlines the two results of the ternary operator inside the for loop, saying that IEnumerable<...> doesn't have a method overload called ThenBy (and similarly for ThenByDescending), but the sortQ will be an IOrderedEnumerable<...> in that block. Why isn't C# type inference picking that up?
Update: for those who might have been confused before, what I left out of the original snippet was that I was assigning sortQ to the result of another query which forced the type inference engine to type sortQ as an IEnumerable<..>, which of course is what was screwing up my example. The answer below is the way around this. Thanks to #Marc for reading between the lines.
I'm guessing sortQ is IEnumerable<T>; you need an IOrderedEnumerable<T> to use ThenBy; fortunately, your first OrderBy returns this, so just catch that:
var result = (sortDirs[0] == "asc") ?
sortQ.OrderBy((d) => d.GetColumnData()[sortCols[0]]) :
sortQ.OrderByDescending((d) => d.GetColumnData()[sortCols[0]]);
if (iSortingCols > 1)
{
for (int i = 1; i < iSortingCols; i++)
{
result = (sortDirs[i] == "asc") ?
result.ThenBy(d => d.GetColumnData()[sortCols[i]]) :
result.ThenByDescending(d => d.GetColumnData()[sortCols[i]]);
}
}