Convert string join utility function into LINQ equivalent - c#

So I have a helper function that takes a particular list, performs a loop and returns a string joined with comma without duplication.
public static string GetJoinedEquipString(List<MeasuredData> dataList)
{
HashSet<string> equipSet = new HashSet<string>();
foreach (MeasuredData data in dataList)
{
equipSet.Add(data.GetEquipNumString());
}
return String.Join(",", equipNumSet.ToArray());
}
Which works fine.. but is there a way to rewrite this hideous looking function using LINQ?

Try:
string.Join(",", dataList.Select(d => d.GetEquipNumString()).Distinct().ToArray());
Instead of using a HashSet to make the list unique, look at Distinct, documented here. This will use the default equality comparer for string (as does your implementation), but there is also an overload that accepts an IEqualityComparer if you are ever using more complex objects.
If you're using .NET 4.0 or newer, you can leave off the call to ToArray since an overload of Join was added that has an IEnumerable<string> as a parameter (as opposed to just string[]).

Related

C# Linq OrderBy based on condition

I have a foreach loop that I want to change:
foreach (var line in lines.OrderBy(x=> x.ColA))
If a condition is met, then instead of ordering by ColA, I want to order by ColB.
I know this could be done like the following:
var orderLines = new List<OrderLines>();
if (condition)
orderLines = lines.OrderBy(x => x.ColB).ToList();
else
orderLines = lines.OrderBy(x => x.ColA).ToList(); ;
foreach (var line in orderLines)
But I am sure there is a more elegant solution.
Several solutions.
(1) Don't do the ToList() before your foreach, only create the IEnumerable.
IEnumerable<OrderLines> orderLines = condition ?
lines.OrderBy(orderLine => orderLine.ColB) :
lines.OrderBy(orderLine => orderLine.ColA);
foreach(OrderLine orderlLine in orderLines) {...}
(2) If you will be using this on several locations, consider to create an extension method. This way your method looks like any other LINQ method.
See extension methods demystified
public static IEnumerable<OrderLine> OrderBy(
this IEnumerable<OrderLine> source,
bool condition)
{
return condition ?
lines.OrderBy(orderLine => orderLine.ColB) :
lines.OrderBy(orderLine => orderLine.ColA);
}
Usage:
If operator checks chexBox1, sort by colB, else sort by colA:
IEnumerable<OrderLine> lines = ...
foreach(var sortedOrderLine in lines.OrderBy(this.CheckBox1.IsChecked))
{
...
}
Because it is an extension method of IEnumerable<OrderLine>, you can even intertwine it with other LINQ methods:
var result = lines.Where(orderLine => orderLine.Date.Year >= 2020)
.OrderBy(this.checkBox1.IsChecked)
.Select(orderLine => new
{
Id = orderLine.Id,
Price = orderLine.Price,
});
But all in all, it doesn't save you a lot of code. The only advantage would be if you would use it in a lot of methods. In that case, a change in how you want to OrderBy condition would have to be changed in only one place. But again: if you expect to use it in one place, moving it to a separate method might not help readers to understand what happens.
That is probably about as good as it gets.
Remember that behind that lambda expression magic happens which (effectively) binds to a Comparer<T> where T depends on the type of the columns being compared.
To make this more terse might make it less efficient. Specifically converting and comparing strings makes it both slower and can get you into trouble (ints sort to 1,2,3,...10,11,... vs their strings to "1","10","11",..."19","2","20","21"...).
A "one-liner" is only elegant if it's behaviour is obvious, otherwise it is obfuscated.
Your code is fine. (IMO;-)
Install NuGet System.Linq.Dynamic and you can pass property name as string to OrderBy like below.
Usage list.AsQueryable().OrderBy("PropertyName1 SortOrder, ropertyName SortOrder"). Where PropertyName will be ColA ColB. And SortOrder will be ASC DESC.
Add using System.Linq.Dynamic;
foreach (var line in lines.AsQueryable().OrderBy(condition ? "ColB" : "ColA")
For .Net Core install NuGet System.Linq.Dynamic.Core.
Add using System.Linq.Dynamic.Core;
foreach (var line in lines.AsQueryable().OrderBy(condition ? "ColB" : "ColA")
For a better practice rather than providing PropertyName as string use nameof(Class.Property) like in your case nameof(OrderLines.ColA). So in case you change ColA property it will show Builderror and you will not get run time exception.
foreach (var line in lines.AsQueryable().OrderBy(condition ? nameof(OrderLines.ColB) : nameof(OrderLines.ColA))
As #AlanK have mentioned, the closest we can get for simplifying OrderBy would be something like:
Func<OrderLines, string> selector = (orderLine) => condition ? orderLine.ColB : orderLine.ColA;
List<OrderLines> orderLines = lines.OrderBy(selector);
provided both ColA and ColB are of same data type. Otherwise it wouldn't be efficient due to the overhead of data type conversion.

Convert [] to List With Constructor

I have a class PlaceInfo that contains a private field of type Result. in the constructor of PlaceInfo i take parameter of type Result.
Now my JsonConvert.DeserializeObject<SearchFind>(JsonData); statement provides me Result[] in res.Results.
I have to construct a List<PlaceInfo>.
The simplest and thumb logical way is given below (that i am using currently).
foreach (var serverPlace in res.Results)
lstPlaces.Add(new PlaceInfo(serverPlace));
Can anyone suggest me advanced constructs?
You can use LINQ:
lstPlaces = res.Results.Select(x => new PlaceInfo(x)).ToList();
remember to add using System.Linq at the top of the file.
You could use the Linq Select and ToList method
Result[] results = ...
List<PlaceInfo> places = results.Select(x => new PlaceInfo(x)).ToList();
The Select method is a projection, applying the given function to all the elements in your array. The ToList method takes the resultant IEnumerable and creates a List.

How to collect a single property in a list of objects?

Is it possible to create an extension method to return a single property or field in a list of objects?
Currently I have a lot of functions like the following.
public static List<int> GetSpeeds(this List<ObjectMotion> motions) {
List<int> speeds = new List<int>();
foreach (ObjectMotion motion in motions) {
speeds.Add(motion.Speed);
}
return speeds;
}
This is "hard coded" and only serves a single property in a single object type. Its tedious and I'm sure there's a way using LINQ / Reflection to create an extension method that can do this in a generic and reusable way. Something like this:
public static List<TProp> GetProperties<T, TProp>(this List<T> objects, Property prop){
List<TProp> props = new List<TProp>();
foreach (ObjectMotion obj in objects) {
props.Add(obj.prop??);
}
return props;
}
Apart from the easiest method using LINQ, I'm also looking for the fastest method. Is it possible to use code generation (and Lambda expression trees) to create such a method at runtime? I'm sure that would be faster than using Reflection.
You could do:
public static List<TProp> GetProperties<T, TProp>(this IEnumerable<T> seq, Func<T, TProp> selector)
{
return seq.Select(selector).ToList();
}
and use it like:
List<int> speeds = motions.GetProperties(m => m.Speed);
it's questionable whether this method is better than just using Select and ToList directly though.
It is, no reflection needed:
List<int> values = motions.Select(m=>m.Speed).ToList();
A for loop would be the fastest I think, followed closely by linq (minimal overhead if you don't do use closures). I can't image any other mechanism would be any better than that.
You could replace the List<int> with a int[] or initialize the list with a certain capacity. That would probably do more to speed up your code than anything else (though still not much).

Linq query against a collection property with a collection parameter

I have a method which takes an array of strings as parameter and queries against a collection property which is also a collection of strings. If that property has one of the values inside the string array passed as parameter, it should be returned.
Here is my code:
public IEnumerable<BlogPost> GetAll(string[] tags,
bool includeUnapprovedEntries = false) {
foreach (var tag in tags) {
foreach (var blogPost in GetAll(includeUnapprovedEntries).
ToList().Where(x => x.Tags.Any(t => t == tag))) {
yield return blogPost;
}
}
}
Note:
Here is the complete code:
https://github.com/tugberkugurlu/MvcBloggy/blob/master/src/MvcBloggy.Data/DataAccess/SqlServer/BlogPostRepository.cs
This does the job but it just doesn't seem right. I could have made this better with some extension methods but couldn't figure out what would do the trick and make this implementation right.
Any idea?
How about this:
public IEnumerable<BlogPost> GetAll(string[] tags,
bool includeUnapprovedEntries = false) {
return GetAll(includeUnapprovedEntries)
.Where(x => x.Tags.Any(t => tags.Contains(t));
}
You may want to call ToList() to materialize the result. Note that this will (hopefully!) result in an IN query in SQL; if you have a large number of tags, I wouldn't be surprised if that failed. (I don't know how the Entity Framework handles that situation.) I believe it should be okay with smaller numbers of tags though.
Note that whether or not this is supported may depend on the version of the entity framework you're using; I seem to remember that some transformations like this (using Contains on a "local" collection) to translate to IN in SQL) have improved over time. Make sure you develop against the same version you'll be deploying against :)

How to change a LINQ list into an arraylist

I have to write a query in a web application using LINQ but I need to change that query into an array list. How can I change the query below to do this?
var resultsQuery =
from result in o["SearchResponse"]["Web"]["Results"].Children()
select new
{
Url = result.Value<string>("Url").ToString(),
Title = result.Value<string>("Title").ToString(),
Content = result.Value<string>("Description").ToString()
};
If you really need to create an ArrayList, you can write new ArrayList(resultsQuery.ToArray()).
However, you should use a List<T> instead, by writing resultsQuery.ToList().
Note that, in both cases, the list will contain objects of anonymous type.
There is a .ToArray() method that'll convert IEnumerable to an Array.
ArrayList doesn't have a constructor or Add(Range) method that takes an IEnumerable. So that leaves two choices:
Use an intermediate collection that does implement ICollection: as both Array and List<T> implement ICollection can be used via the ToArray() or ToList() extension methods from LINQ.
Create an instance of ArrayList and then add each element of the result:
var query = /* LINQ Expression */
var res = new ArrayList();
foreach (var item in query) {
res.Add(item);
}
The former method is simple to do but does mean creating the intermediate data structure (which of the two options has a higher overhead is an interesting question and partly depends on the query so there is no general answer). The latter is more code and does involve growing the ArrayList incrementally (so more memory for the GC, as would be the case for an intermediate Array or List<T>).
If you just need this in one place you can just do the code inline, if you need to do it in multiple places create your own extension method over IEnumerable<T>:
public static class MyExtensions {
public static ArrayList ToArrayList<T>(this IEnumerable<T> input) {
var col = input as ICollection;
if (col != null) {
return new ArrayList(col);
}
var res = new ArrayList();
foreach (var item in input) {
res.Add(item);
}
return res;
}
}

Categories