LINQ that contains symbol only once - c#

Need your help: I have Datable that has datarows like :
test1
test1:1
test1:1:1
test1:2
I need to select only rows that contain ":" only once.
Result should be like :
test1:1
test1:2
Any ideas how to make it ??
I stuck after :
var result = dTable.AsEnumerable().Where(dr => dr.Field<string>("Name").Contains(":"));
,where "Name" is a column Name.
Thank you in advance

var result = dTable.AsEnumerable()
.Where(dr => dr.Field<string>("Name").Count(z=>z==':')==1);
or
var result = dTable.AsEnumerable()
.Where(dr => dr.Field<string>("Name").Where(z=>z==':').Count()==1);
or
var result = dTable.AsEnumerable()
.Where(dr => dr.Field<string>("Name").IndexOf(':') == dr.Field<string>("Name").LastIndexOf(':') && dr.Field<string>("Name").Contains(":"));

You can convert the content of the Field to a Char array and then count the number of times you get the ':'
var result = dt.AsEnumerable()
.Where(dr => dr.Field<string>("Name")
.ToCharArray()
.Count(c => c == ':') == 1);

Try this instead:
var result = dTable.AsEnumerable().Where(dr => dr.Field<string>("Name").Count(f => f == ':') == 1);
With LINQ, this is very easy using x.Count().
If you want to do it without LINQ, try this:
var result = dTable.AsEnumerable().Where(dr => dr.Field<string>("Name").Split(':').Length - 1 == 1);
See the top answer at this other Stack Overflow question, which has something similar. I don't usually use LINQ, so forgive me if my code doesn't work. Hope this helps!

Related

get data from datatable query

I have been trying to get the results for the query below and add the data into a list so that I can remove duplicates
var w = sqlData.AsEnumerable().Where(data => data.Field<String>("slideNo") == "5")
.Select(data=> data.Field<String>("QuestionStartText"));
this information give out data based on column and I want to go though the variable and put each inderviual string into a lsit
return sqlData
.AsEnumerable()
.Where(data => data.Field<String>("slideNo") == "5"))
.Select(data=> data.Field<String>("QuestionStartText"))
.Distinct()
.ToList();
Try This:-
var w = sqlData.AsEnumerable().Where(data => data.Field<String>("slideNo") == "5")
.Select(data=> data.Field<String>("QuestionStartText")).Distinct().ToList();

Sorting a list of strings by placing words starting with a certain letter at the start

Assuming I have the following list:
IList<string> list = new List<string>();
list.Add("Mouse");
list.Add("Dinner");
list.Add("House");
list.Add("Out");
list.Add("Phone");
list.Add("Hat");
list.Add("Ounce");
Using LINQ how would I select the words containing "ou" and sort the selection such that the words beginning with "ou" are listed at the start and then the words containing but not starting with "ou" are subsequently listed. The list I'm trying to create would be:
Ounce
Out
House
Mouse
I came up with the following but it is not working:
list.Where(x => x.Contains("ou"))
.OrderBy(x => x.StartsWith("ou"))
.Select(x => x);
You're getting a case-sensitive comparison, and also you need OrderByDescending(). A quick and dirty way to achieve the case-insensitivity is ToLowerInvariant():
var result = list.Where(x => x.ToLowerInvariant().Contains("ou"))
.OrderByDescending(x => x.ToLowerInvariant().StartsWith("ou"))
.Select(x => x);
Live example: http://rextester.com/GUR97180
This previous answer shows the correct way to do a case insensitive comparison (ie, dont use my example above, its bad)
Your first mistake is not comparing strings in a case-insensitive way; "Out" and "Ounce" have capital Os and would not return "true" when you use Contains("ou"). The solution is to use ToLower() when checking letters.
list.Where(x => x.ToLower().Contains("ou"))
.OrderByDescending(x => x.ToLower.StartsWith("ou")) //true is greater than false.
.Select(x => x);
Three problems:
You need to assign the result to something, otherwise it is simply discarded.
You need to use OrderByDescending because true sorts after false if you use OrderBy.
You need to use a case-insensitive compare.
Try this:
var needle = "ou";
var stringComparison = StringComparison.OrdinalIgnoreCase;
var query =
from word in list
let index = word.IndexOf(needle, stringComparison)
where index != -1
orderby index
select word;
This will append an empty space to the beginning of words that start with "OU".
var result = list.Where(x => x.ToLowerInvariant().Contains("ou"))
.OrderBy(x => x.ToLowerInvariant()
.StartsWith("ou") ? " " + x : x.Trim());
list = list.Where(x => x.ToLower().Contains("ou"))
.OrderBy(x => !x.ToLower().StartsWith("ou")).ToList();
Or by using the methods of List (changing it from IList to List):
list.RemoveAll(x => !x.ToLower().Contains("ou"));
list.Sort((s1, s2) => -1 * 1.ToLower().StartsWith("ou")
.CompareTo(s2.ToLower().StartsWith("ou")));
I think this is what you're looking for:
list = list.Where(x => x.IndexOf("ou", StringComparison.OrdinalIgnoreCase) >= 0)
.OrderByDescending(x => x.StartsWith("ou", StringComparison.OrdinalIgnoreCase))
.ThenBy(x => x)
.ToList();
Note that instead of converting the strings ToLower (or upper), I use a StringComparison enum (currently OrdinalIgnoreCase). This ensures that it works consistently as expected in any culture. Choose the right case-insensitive comparison depending on your circumstance.
If you prefer the LINQ query syntax that's:
list = (from x in list
where x.IndexOf("ou", StringComparison.OrdinalIgnoreCase) >= 0
orderby x.StartsWith("ou", StringComparison.OrdinalIgnoreCase) descending, x
select x).ToList();
var bla = "ou";
var list = new List<string>{
"Mouse",
"Dinner",
"House",
"Out",
"Phone",
"Hat",
"Ounce"};
var groupa = list.GroupBy(x =>x.ToLower().Contains(bla));
groupa.First().ToList().OrderByDescending(x => x.ToLower().StartsWith(bla));
You can simply call the list.Sort method by passing in an instance of a custom comparer as follows:
public class MyCustomStringComparer: IComparer<string>
{
public int Compare(Entity x, Entity y)
{
int result = 0;
if (x.ToLower().StartsWith("ou") && y.ToLower().StartsWith("ou"))
result = x.Compare(y);
else if (x.ToLower().StartsWith("ou") && !y.ToLower().StartsWith("ou"))
result = -1;
else if (!x.ToLower().StartsWith("ou") && y.ToLower().StartsWith("ou"))
result = 1;
else
result = x.Compare(y);
return (result);
}
}

How can I concatenate a where clause using OR on LINQ?

I have this code :
foreach (Package pack in Packages)
{
filteredResults = filteredResults.Where(o => o.ID == pack.ID);
}
the only problems is that I filter the result N time (so N where).
What I'd like to do is to filter the result only one time (only a where clause) with N expression. Somethings like :
Where o.ID == pack.ID OR o.ID == pack.ID OR o.ID == pack.ID OR o.ID == pack.ID...
Is it possible to do this with LINQ?
Something like the code below should work, or at least steer you in the right direction.
-- Get all the package IDs you want to select on.
var packIDs = from pack in Packages
select pack.ID;
-- Return all results where the ID is in the package ids above.
filteredResults = from result in filteredResults
where packIDs.Contains(result.ID)
select result;
The above assumes your and's were a logic mistake and you meant ors.
var packIds = Packages.Select(x=>x.ID).ToArray();
filteredResults = filteredResults.Where(o=> packIds.Contains(o.ID));
If this is linq to sql this will get translated into:
WHERE ID IN (1,2,3,4)
Something like this might help you:
filteredResults = originalResults.Where(o => Packages.Any(p => p.ID == o.ID));
Do you not want Intersect()? i.e
var ids = filteredResults.Select( fr => fr.Id ).Intersect(Packages.Select( p => p.PackID ) ) ;
I think you need to use expression with LinqKit
var v = from utente in db.Utente
select utente;
Expression<Func<Utente, bool>> expr = c => c.Age == 26;
expr = expr.Or<Utente>(c => c.Name != "Matteo");
v = v.Where(expr.Expand());
The result is:
SELECT...... FROM......
WHERE (26 = [Extent1].[Age ]) OR ('Matteo' <> [Extent1].[Name])
I have the same issue, i try this solution

Combining two simple related linq queries

I have two queries and i'm using the result of the first one in the second one like this
var temp = (ObjectTable.Where(o => o.Category == "Y"));
var anonymousObjList = temp.Select(o => new {o, IsMax = (o.Value == temp.Max(x => x.Value))});
Is there a way to combine these into one query?
EDIT:
I cannot just chain them directly because I'm using temp.Max() in the second query.
Why? it would be clearer (and more efficient) to make it three:
var temp = (ObjectTable.Where(o => o.Category == "Y"));
int max = temp.Max(x => x.Value);
var anonymousObjList = temp.Select(o => new {o, IsMax = (o.Value == max)});
You can do it in one statement using query syntax, using the let keyword. It only evaluates the 'max' once, so it just like the three separate statements, just in one line.
var anonymousObjList = from o in ObjectTable
where o.Category == "Y"
let max = ObjectTable.Max(m => m.Value)
select new { o, IsMax = (o.Value == max) };
This is the only time I ever use query syntax. You can't do this using method syntax!
edit: ReSharper suggests
var anonymousObjList = ObjectTable.Where(o => o.Category == "Y")
.Select(o => new {o, max = ObjectTable.Max(m => m.Value)})
.Select(#t => new {#t.o, IsMax = (#t.o.Value == #t.max)});
however this is not optimal. The first Select is projecting a max Property for each item in ObjectTable - the Max function will be evaluated for every item. If you use query syntax it's only evaluated once.
Again, you can only do this with query syntax. I'm not fan of query syntax but this makes it worthwhile, and is the only case in which I use it. ReSharper is wrong.
Possibly the most straightfirward refactoring is to replace all instances of "temp" with the value of temp. Since it appears that this value is immutable, the refactoring should be valid (yet ugly):
var anonymousObjList = ObjectTable.Where(o => o.Category == "Y")
.Select(o => new {o, IsMax = (o.Value == ObjectTable.Where(o => o.Category == "Y").Max(x => x.Value))});
As has already been pointed out, this query really has no advantages over the original, since queries use deffered execution and can be built up. I would actually suggest splitting the query even more:
var temp = (ObjectTable.Where(o => o.Category == "Y"));
var maxValue = temp.Max(x => x.Value);
var anonymousObjList = temp.Select(o => new {o, IsMax = (o.Value == maxValue)});
This is better than the original because every time "Max" is called causes another iteration over the entire dataset. Since it is being called in the Select of the original, Max was being called n times. That makes the original O(n^2)!

Need a linq query where inclusion indicates property matches all values in secondary list

I want to duplicate the following logic in a single query.
var currentRows = resultsTable.AsEnumerable();
foreach (var wholeWord in excludeWholeWords)
{
currentRows = from row in currentRows
where !FoundWholeWord(wholeWord, row.Field<string>("busdescl"))
select row;
}
resultsTable = currentRows.CopyToDataTable();
I had tried the following, but it results in matching if !FoundWholeWord is true for any wholeWord, instead of my intent (which is that a match means !FoundWholeWord is true for ALL items in excludeWholeWords
var matchGvRows = (from wholeWord in excludeWholeWords
from row in gvkeysTable.AsEnumerable()
where !FoundWholeWord(wholeWord, row.Field<string>("busdescl"))
select row).Distinct();
Any ideas?
If I understand the question correctly, it should be something along the lines of:
var newRows = currentRows
.Where(r => !excludeWholeWords.Any(w => w == r.Field<string>("busdescl"));
I don't know what the FoundWholeWord is but if it does anything different than just comparing strings, you can use it like:
var newRows = currentRows
.Where(r => !excludeWholeWords.Any(w => FoundWholeWord(w, r.Field<string>("busdescl")));
How about this?
var matchGvRows = excludeWholeWords.Aggregate(currentRows, (current, wholeWord) => current.Where(row => !FoundWholeWord(wholeWord, row.Field<string>("busdescl"))));
currentRows = excludeWholeWords.Aggregate(currentRows, (current, wholeWord) => (from row in current
where !FoundWholeWord(wholeWord, row.Field<string>("busdescl"))
select row));
That's what ReSharper's "Convert to LINQ expression" does.

Categories

Resources