Using ternary operator inside contains in lamba expression - c#

I have a requirement to find records that does not contain value from studentids list. There is no 1:1 matching field. The records contain 3 key fields and based on condition, I have to compare against the studentids list.
For example: If rawdata record has non empty studentnumber value, then I have to concatenate studentnumber and sequencenumber and check if that combination exists in studentids list. Otherwise, I have to concatenate EnrollNumber and SequenceNumber.
I am trying to use ternary operator inside a contains as shown below,
var studentIDs = students.Select(x => x.StudentID).Distinct().ToList();
var rawData = StudentManager.GetAllData();
var resultList = rawData.Where(x => !studentIDs.Contains($"{(!string.IsNullOrWhiteSpace(x.StudentNumber)? (x.StudentNumber+x.SequenceNumber):(x.EnrollNumber+x.SequenceNumber))}")).ToList();
However, for larger dataset (more than 5K), it seems getting slower. Any suggestion for alternate method or way to improve will be greatly appreciated. Especially, if the code (Ternary operator) inside Contains can be simplified.

As #derpischer mention on the commend, Did you try with a HashSet?
Replace the firs line for the following:
var studentIDs = new HashSet<string>(students.Select(x => x.StudentID));
This will speed up your execution times. Please let me know if it works.

I think your approach to the logic is fine. I think you can present it in a clearer and easier way. Consider the below:
HashSet<string> studentIDs = students.Select(s => s.StudentID)
.ToHashSet();
string StudentID(RawDataStudent s) => string.IsNullOrWhiteSpace(s.StudentNumber)
? $"{s.EnrollNumber}{s.SequenceNumber}"
: $"{s.StudentNumber}{s.SequenceNumber}";
var rawData = StudentManager.GetAllData();
var resultList = rawData.Where(s => !studentIDs.Contains(StudentID(s)))
.ToList();
Important points:
Pulling the entire 'contains' lambda out and presenting it as a clearly named function with intent - good readability
Always try and work in the affirmative with booleans - specifically you ended up with this weird bracketed negation with the null or whitespace, just switch the returns around - again also easier to read
As other posters have commented, calling contains against a HashSet will be considerably faster
Note I've assumed your GetAllData return type somewhat - good example of when var is a bit evil

Related

Is there a way to specify a variable number of Observable in Observable.WhenAll()?

I'm applying reactive programming to the Unity3D project.
Is there a way to specify a variable number of Observable in Observable.WhenAll()?
Sample or search results suggest an explicit way to enter all non-variable items, most of which are not variable numbers.
var parallel = Observable.WhenAll(
ObservableWWW.Get ("http://google.com/"),
ObservableWWW.Get ("http://bing.com/"),
ObservableWWW.Get ("http://unity3d.com/");
What I want is as follows.
List<string> URLs = new List<string>();
URLs.Add("http://google.com/");
URLs.Add("http://bing.com/");
...
...
var parallel = Observable.WhenAll( ??? //ObservableWWW.Get() : count of URLs list);
Please reply.
Thank you.
WhenAll(this IEnumerable> sources) already does this. I suspect the real question is how to produce some observables from a list of URLs. One way to do it would be to use LINQ :
var urlObservables=URLs.Select(url=>ObservableWWW.Get(url));
var allAsOne= Observable.WhenAll(urlObservables);
Update
As Felix Keil commented, what if the OP wants to observe only a few of those observables? That's a job for LINQ's Take(), applied either on the URL list or the observables list, eg :
var someObservables=URLs.Take(2).Select(url=>ObservableWWW.Get(url));
or even
var someObservables=URLs.Select(url=>ObservableWWW.Get(url)).Take(2);
LINQ's lazy evaluation means the operators will run only when someObservables gets enumerated. When that happens, enumeration will stop after the first two iterations so ObservableWWW.Get will be called only twice

Loop - Calculated last element different

Hi everyone (sry for the bad title),
I have a loop in which I can get a rounding difference every time I pass. I would like to cumulate them and add it to the last record of my result.
var cumulatedRoundDifference = 0m;
var resultSet = Enumerable.Range(0, periods)
.Select(currentPeriod => {
var value = this.CalculateValue(currentPeriod);
var valueRounded = this.CommercialRound(value);
// Bad part :(
cumulatedRoundDifference += value - valueRounded;
if (currentPeriod == periods - 1)
valueRounded = this.CommercialRound(value + valueRounded);
return valuesRounded;
}
At the moment the code of my opinion is not so nice.
Is there a pattern / algorithm for such a thing or is it somehow clever with Linq, without a variable outside the loop?
many Greetings
It seems like you are doing two things - rounding everything, and calculating the total rounding error.
You could remove the variable outside the lambda, but then you would need 2 queries.
var baseQuery = Enumerable.Range(0, periods)
.Select(x => new { Value = CalculateValue(x), ValueRounded = CommercialRound(x) });
var cumulateRoundDifference = baseQuery.Select(x => x.Value - x.ValueRounded).Sum();
// LINQ isn't really good at doing something different to the last element
var resultSet = baseQuery.Select(x => x.ValueRounded).Take(periods - 1).Concat(new[] { CommercialRound(CalculateValue(periods - 1) + CommericalRound(periods - 1)) });
Is there a pattern / algorithm for such a thing or is it somehow clever with Linq, without a variable outside the loop?
I don't quite agree with what you're trying to accomplish. You're trying to accomplish two very different tasks, so why are you trying to merge them into the same iteration block? The latter (handling the last item) isn't even supposed to be an iteration.
For readability's sake, I suggest splitting the two off. It makes more sense and doesn't require you to check if you're on the last loop of the iteration (which saves you some code and nesting).
While I don't quite understand the calculation in and of itself, I can answer the algorithm you're directly asking for (though I'm not sure this is the best way to do it, which I'll address later in the answer).
var allItemsExceptTheLastOne = allItems.Take(allItems.Count() - 1);
foreach(var item in allItemsExceptTheLastOne)
{
// Your logic for all items except the last one
}
var theLastItem = allItems.Last();
// Your logic for the last item
This is in my opinion a cleaner and more readable approach. I'm not a fan of using lambda methods as mini-methods with a less-than-trivial readability. This may be subjective and a matter of personal style.
On rereading, I think I understand the calculation better, so I've added an attempt at implementing it, while still maximizing readability as best I can:
// First we make a list of the values (without the sum)
var myValues = Enumerable
.Range(0, periods)
.Select(period => this.CalculateValue(period))
.Select(period => period - this.CommercialRound(period))
.ToList();
// myValues = [ 0.1, 0.2, 0.3 ]
myValues.Add(myValues.Sum());
// myValues = [ 0.1, 0.2, 0.3, 0.6 ]
This follows the same approach as the algorithm I first suggested: iterate over the iteratable items, and then separately handle the last value of your intended result list.
Note that I separated the logic into two subsequent Select statements as I consider it the most readable (no excessive lambda bodies) and efficient (no duplicate CalculateValue calls) way of doing this. If, however, you are more concerned about performance, e.g. when you are expecting to process massive lists, you may want to merge these again.
I suggest that you always try to default to writing code that favors readability over (excessive) optimization; and only deviate from that path when there is a clear need for additional optimization (which I cannot decide based on your question).
On a second reread, I'm not sure you've explained the actual calculation well enough, as cumulatedRoundDifference is not actually used in your calculations, but the code seems to suggest that its value should be important to the end result.

In C# Convert List<dynamic> to List<string>

Suppose I have a List<dynamic> object containing strings:
var dlist = new List<dynamic>()
{
"test",
"test2",
"test3"
};
Is there any efficient way of converting this into a proper List<string> object? I know I can iterate over this list and cast each element to a string, then add this to the result list, but maybe some Linq magic could do the trick in one line?
I tried using some Select() combined with ToList() and Cast<string>, but to no avail. How should this be done properly?
Note: By saying "efficient" I mean of course number of lines of code. I do not take execution time or performance into account. Also - let's suppose I do not need to type check, there will always be strings only in this dynamic list.
EDIT: Okay, so in regards to comments on "why Cast wasn't working for you" - looks like I had another problem regarding the data I receive (I'm using Dapper) and that's why it didn't work. Sorry for the confusion, I thought my list converting was wrong while the problem was not related to this.
Given
var dList = new List<dynamic>() { /*...initialize list */ };
If you are interested in extracting all the strings in the collection, ignoring all other types, you can use:
// Solution 1: Include only strings, no null values, no exceptions thrown
var strings = dlist.OfType<string>().ToList();
If you are certain that all the items in the list are strings (it will throw an exception if they are not), you can use:
// Solution 2: Include strings with null values, Exception for other data types thrown
var strings = dlist.Cast<string>().ToList();
If you want the default string representation, with null for null values, of all the items in the list, you can use:
// Solution 3: Include all, regardless of data type, no exceptions thrown
var strings = dlist.Select(item => item?.ToString()).ToList();
This answer is for dart/flutter
Given
List<dynamic> dList;
You can use
var sList = List<String>.from(dlist);
to convert a List<dynamic> to List<String>

Getting a list item by index

I've recently started using c# moving over from Java. I can't seem to find how to get a list item by index. In java to get the first item of the list it would be:
list1.get(0);
What is the equivalent in c#?
list1[0];
Assuming list's type has an indexer defined.
You can use the ElementAt extension method on the list.
For example:
// Get the first item from the list
using System.Linq;
var myList = new List<string>{ "Yes", "No", "Maybe"};
var firstItem = myList.ElementAt(0);
// Do something with firstItem
Visual Basic, C#, and C++ all have syntax for accessing the Item property without using its name. Instead, the variable containing the List is used as if it were an array:
List[index]
See, for instance, List.Item[Int32] Property.
.NET List data structure is an Array in a "mutable shell".
So you can use indexes for accessing to it's elements like:
var firstElement = myList[0];
var secondElement = myList[1];
Starting with C# 8.0 you can use Index and Range classes for accessing elements. They provides accessing from the end of sequence or just access a specific part of sequence:
var lastElement = myList[^1]; // Using Index
var fiveElements = myList[2..7]; // Using Range, note that 7 is exclusive
You can combine indexes and ranges together:
var elementsFromThirdToEnd = myList[2..^0]; // Index and Range together
Also you can use LINQ ElementAt method but for 99% of cases this is really not necessary and just slow performance solution.
Old question, but I see that this thread was fairly recently active, so I'll go ahead and throw in my two cents:
Pretty much exactly what Mitch said. Assuming proper indexing, you can just go ahead and use square bracket notation as if you were accessing an array. In addition to using the numeric index, though, if your members have specific names, you can often do kind of a simultaneous search/access by typing something like:
var temp = list1["DesiredMember"];
The more you know, right?
you can use index to access list elements
List<string> list1 = new List<string>();
list1[0] //for getting the first element of the list

Comparing 2 lists using Linq

I have 2 lists I am trying to compare. I execute the following and I get a false value returned:
var areIdentical = list1.SequenceEqual(list2, myFileCompare);
That part is working. My lists are NOT equal. The problem is, I'm using the following command to try to find the differences:
var fileDiff = (from file in list1
select file).Except(list2, myFileCompare);
My problem is, fileDiff is returning an empty result set. Since I know they are NOT identical, shouldn't I get something returned? Perhaps my query is wrong on this. Any help would be appreciated! By the way, I can post more of my code, if you really need it, however, this should suffice.
You wouldn't get anything if:
list2 contained everything in list1 but also extra items
the ordering was different
Assuming you don't care about the ordering, you can use:
var extraItemsInList2 = list2.Except(list1);
var extraItemsInList1 = list1.Except(list2);
If you do care about the order, you'll need to work out exactly how you want the differences to be represented.
SequenceEqual() will return true only if the elements as well as the element sequence is the same.
Except() will compare only the elements, not the sequence.
Your two lists obviously have different sequences but, judging by the behavior you've posted, I'm guessing they both contain the same elements.
If you are after the symmetric difference (all differences between either list, ordering is not important) then you could use the following which will be heavy on the computation but should do the trick:
var fileDiff = list1.Union(list2).Except(list1.Intersect(list2));
Or (as per Jon Skeet's answer):
var fileDiff = list1.Except(list2).Union(list2.Except(list1));
I'll leave it to the rest of the community to show you a more efficient way to do it... But this is the most obvious "linq" way that I can think of...
SequenceEqual cares about sequence (which is kind of hinted in its name ;) ), but Except doesn't.
So it is entirely possible that list2 contains same elements as list1, but in different order, so SequenceEqual returns false yet Except returns no elements.
It is also possible that list2 is a proper super-set of the list1, in which case SequenceEqual returns false regardless of order, and Except still returns no elements.
If you want to work with set operations, you'll probably be better off using some set-like container such as HashSet or SortedSet directly. In your case, you might be interested in HashSet.SetEquals and/or HashSet.ExceptWith.

Categories

Resources