Trim function in Linq GroupBy statement - c#

I would like to GroupBy element using a field (CompteNum) that has space in it. So I would like to trim the field using this code
var excelLinesGrouped = excelLines
.GroupBy(l => l.CompteNum.Trim(),
(entry, elements) =>
new
{
Entry = entry,
Writings = elements
});
It gives null results probably meaning that trim is not supported in GroupBy. Is there any alternative for this?
Thanks

Related

Linq return all records from entity where a field contains one or more words from a list

Given a List<string> How to return all records in an entity that has a field containing one or more words from the list.
I tried the below which does not work and I'm starting to go around in circles a bit:
List<string> searchwords = new List<string>() {"word1","word2"};
var results = context.activities
.Where(a => a.Title.Contains(searchwords.Any().ToString())).ToList();
The problem with your current code:
var results = context.activities.Where(a =>
a.Title.Contains(searchwords.Any().ToString())).ToList();
is that you have your needle and haystack backwards. The fact that you needed to call .ToString() on Any() should have tipped you off. Any() returns a bool, which you're casting to a string, so you're just checking whether Title contains the string "True". Definitely not what you want. You want something closer to:
var results = context.activities
.Where(a => searchwords.Any(searchWord => a.Title.Contains(searchWord)));

Select multiple types in LINQ Select

Can this be turned into a select statement?
foreach (var gf in CreateGenericFieldsOnInspection(model))
{
simpleInspection.GenericFields.Add(gf.GenericFieldDefinition.Name,
new LC360Carrier.Domain.Models.Import.GenericField
{
GenericFieldType = GenericFieldValueType.Text,
Value = gf.Value
});
}
It looks like GenericFields is a Dictionary<string, GenericFieldOrSomething>. You could contort this into something really weird for the sake of using LINQ. But the purpose of LINQ is to query one or more IEnumerables to either get a result or transform them into something else.
It's just like SQL. You query it to either get a set of records or some value like the sum of some numbers.
In your case you've already got a result set - whatever CreateGenericFieldsOnInspection(model) returns. It makes sense to do what you're already doing - foreach through the results and perform some action on each one of them.
LINQ would be handy if you needed to query that set. For example,
var filteredProperties = CreateGenericFieldsOnInspection(model)
.Where(property => property.Name.StartsWith("X"));
But even then, once you had that collection, it would still make sense to use a foreach loop.
You'll see this sometimes - I did it when I first learned LINQ:
CreateGenericFieldsOnInspection(model).ToList()
.ForEach(property => DoSomethingWith(property));
We convert something to a List because then we can use .ForEach. But there's no benefit to it. It's just foreach with different syntax and an extra step.
I have an extension method that permits add. I was just having trouble w the syntax. Bagus Tesa, above was also helpful. Thanks.
simpleInspection.GenericFields = simpleInspection.GenericFields.Union(CreateGenericFieldsOnInspection(model).Select(x => new KeyValuePair<string, object>(x.GenericFieldDefinition.Name, new LC360Carrier.Domain.Models.Import.GenericField
{
GenericFieldType = GenericFieldValueType.Text,
Value = x.Value
}))).ToDictionary(x => x.Key, x => x.Value);

why is this linq query return a boolean and not the first result of the select?

I have a string array with 5 items. How to get one of these 5 items by a linq query?
Code below returns only a boolean true.
string[] allWebTemplateSettings =SiteLidmaatschapSettings.Current.ProvisioningSettings;
var webTemplate = allWebTemplateSettings
.Select(x => x.StartsWith(string.Format("Template:{0}", web.WebTemplate)))
.FirstOrDefault();
Use Where instead of Select:
var webTemplate = allWebTemplateSettings.Where(x => x.StartsWith(string.Format("Template:{0}", web.WebTemplate))).FirstOrDefault();
Well, you're getting an IEnumerable of bools with your Select, then you pick the first one if there are any. That's why you're getting a bool as your answer.
I think what you actually want is this:
string[] allWebTemplateSettings = SiteLidmaatschapSettings.Current.ProvisioningSettings;
var prefix = string.Format("Template:{0}", web.WebTemplate);
var webTemplate = allWebTemplateSettings
.FirstOrDefault(x => x.StartsWith(prefix));
I've moved the string formatting operation out of the predicate since it is wasteful to recompute it for each element in your collection (especially if the collection is long).
You are confusing Select, which selects a new value based on each existing value of a sequence, with Where, which filters a sequence so it only contains items where a condition is met.
The simplest change is to replace your usage of Select with Where.
string[] allWebTemplateSettings = SiteLidmaatschapSettings.Current.ProvisioningSettings;
var webTemplate = allWebTemplateSettings
.Where(x => x.StartsWith(string.Format("Template:{0}", web.WebTemplate)))
.FirstOrDefault();
The other answers have rolled this usage of Where into FirstOrDefault without explaining your underlying confusion.
That's because StartsWith returns a bool and you're saying Select that bool based on whether it starts with that value or not. So actually, you're not even filtering on that value at all because you're not using a filter expression.
Actually, you only need FirstOrDefault as the list is already a List<string>
string[] allWebTemplateSettings = SiteLidmaatschapSettings.Current.ProvisioningSettings;
var webTemplate = allWebTemplateSettings
.FirstOrDefault(x => x.StartsWith(string.Format("Template:{0}", web.WebTemplate)));

Linq remove all

Im trying to replace items in a list with the RemoveAll function, but didnt realize that it completely removes the index of the list. Is there a function that can replace objects instead of remove them? I am comparing strings.
Items.RemoveAll(x => commonItems.Any(y => y.ItemName == x.ItemName));
Or maybe once its removed I replace it with another empty index at that same spot?
You could replace RemoveAll with Where to get the records you want to modify first:
var matches = Items.Where(x => commonItems.Any(y => y.ItemName == x.ItemName));
Then just iterate through the results and replace the value:
foreach (var match in matches)
match.ItemName = "N/A";

Check array for duplicates, return only items which appear more than once

I have an text document of emails such as
Google12#gmail.com,
MyUSERNAME#me.com,
ME#you.com,
ratonabat#co.co,
iamcool#asd.com,
ratonabat#co.co,
I need to check said document for duplicates and create a unique array from that (so if "ratonabat#co.co" appears 500 times in the new array he'll only appear once.)
Edit:
For an example:
username1#hotmail.com
username2#hotmail.com
username1#hotmail.com
username1#hotmail.com
username1#hotmail.com
username1#hotmail.com
This is my "data" (either in an array or text document, I can handle that)
I want to be able to see if there's a duplicate in that, and move the duplicate ONCE to another array. So the output would be
username1#hotmail.com
You can simply use Linq's Distinct extension method:
var input = new string[] { ... };
var output = input.Distinct().ToArray();
You may also want to consider refactoring your code to use a HashSet<string> instead of a simple array, as it will gracefully handle duplicates.
To get an array containing only those records which are duplicates, it's a little moe complex, but you can still do it with a little Linq:
var output = input.GroupBy(x => x)
.Where(g => g.Skip(1).Any())
.Select(g => g.Key)
.ToArray();
Explanation:
.GroupBy group identical strings together
.Where filter the groups by the following criteria
.Skip(1).Any() return true if there are 2 or more items in the group. This is equivalent to .Count() > 1, but it's slightly more efficient because it stops counting after it finds a second item.
.Select return a set consisting only of a single string (rather than the group)
.ToArray convert the result set to an array.
Here's another solution using a custom extension method:
public static class MyExtensions
{
public static IEnumerable<T> Duplicates<T>(this IEnumerable<T> input)
{
var a = new HashSet<T>();
var b = new HashSet<T>();
foreach(var x in input)
{
if (!a.Add(x) && b.Add(x))
yield return x;
}
}
}
And then you can call this method like this:
var output = input.Duplicates().ToArray();
I haven't benchmarked this, but it should be more efficient than the previous method.
You can use the built in in .Distinct() method, by default the comparisons are case sensitive, if you want to make it case insenstive use the overload that takes a comparer in and use a case insensitive string comparer.
List<string> emailAddresses = GetListOfEmailAddresses();
string[] uniqueEmailAddresses = emailAddresses.Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
EDIT: Now I see after you made your clarification you only want to list the duplicates.
string[] duplicateAddresses = emailAddresses.GroupBy(address => address,
(key, rows) => new {Key = key, Count = rows.Count()},
StringComparer.OrdinalIgnoreCase)
.Where(row => row.Count > 1)
.Select(row => row.Key)
.ToArray();
To select emails which occur more then once..
var dupEmails=from emails in File.ReadAllText(path).Split(',').GroupBy(x=>x)
where emails.Count()>1
select emails.Key;

Categories