I'm using LINQ to create dynamic sql, when I'm using contains I don't want it to prefix and suffix % and if I'm using % inside my string I don't want to escape it. It escapes the percentage signs added by me using ~ as prefix before % as escape sequence character
For instance:
string str = '%test%.doc%'
.Contains(str) // converts this into LIKE '%~%test~%.doc~%%'
Expected Conversion: LIKE '%test%.doc%%'
as questioner asked, I've made my comments an answer
See Using LINQ Contains vs. SqlMethods.Like and in general the SqlMethods.Like method which will enable you to do a custom LIKE with Linq-to-sql.
Simple example:
var res = from row in dc.Table
where SqlMethods.Like(row.Column, "%A%A%")
select row;
More examples with Contains,StartsWith and Like: http://blogs.microsoft.co.il/blogs/bursteg/archive/2007/10/16/linq-to-sql-like-operator.aspx
Contains is probably translating into the use of the LIKE operator in SQL. This operator takes % as a wildcard character. Contains("abc") maps to LIKE '%abc%'.
I use the following extensions to avoid that case (although in my specific case, I'm still using wildcards, but you could modify it for your own effect).
public static bool Like(this string value, string term)
{
Regex regex = new Regex(string.Format("^{0}$", term.Replace("*", ".*")), RegexOptions.IgnoreCase);
return regex.IsMatch(value ?? string.Empty);
}
public static IEnumerable<string> Like(this IEnumerable<string> source, string expression)
{
return (from s in source where s.Like(expression) select s);
}
Unfortunately, I can't think of an easy way to do this, but this might work:
var a = from t in Db.Tests
let i1 = t.Name.IndexOf("test")
let i2 = t.Name.IndexOf(".doc")
where i1 != -1 && i2 != -1 && i1 < i2
select t;
Here is the equivalent in method chains:
Db.Tests.Select(t => new {t, i1 = t.Name.IndexOf("test")}).Select(
#t1 => new {#t1, i2 = #t1.t.Name.IndexOf(".doc")}).Where(
#t1 => #t1.#t1.i1 != -1 && #t1.i2 != -1 && #t1.#t1.i1 < #t1.i2).Select(#t1 => #t1.#t1.t);
Related
I want to do a kind-of a BETWEEN operation between 2 SQL queries with LINQ.
I have a collection of Person surnames.
And another collection that contains a collection of beginning and ending value of surnames to search for.
For example, let say I have a range in a table to get the number of Persons with Surnames starting between BA and BE.
var person = dc.Persons.Select(p => p.Surname);
var range = from r in dc.Ranges
select new
{
ID = r.ID,
StartRange = r.StartValue, //e.g. BA
EndRage = r.EndValue //e.g. BE
};
Now I need the number of persons in every range.
var PersonCount = from r in range
select new
{
r.ID,
NumberOfPersonsInRange = person.Where(t => t.Surname >= r.StartRange
&& t.Surname <= r.EndRage).Count()
//This does not work. Gives error that Operator '>=' cannot be applied to operands of type 'string' and 'string'
}
If I understand your question correctly, you can use string.CompareTo method:
person.Count(t => t.Surname.CompareTo(r.StartRange) >= 0)
&& t.Surname.CompareTo(r.EndRage) <= 0)
An alternative to the CompareTo() (instance) method would be to use the Compare() (static) method, which also allows you to specify a Boolean argument to specify a case-insensitive comparison if you like.
This method does an alphabetic comparison between the two strings, and returns -1 if the first string is less than the second string, 0 if they are equal, and 1 if the first string is greater than the second string.
Here's an example of your code which does a case-insensitive comparison to determine if the surname is within the range (note you can also replace the Where with Count):
var personCount = from r in range
select new
{
r.ID,
NumberOfPersonsInRange = person.Count(p =>
string.Compare(p.Surname, r.StartRange, true) > -1 &&
string.Compare(p.Surname, r.EndRange, true) < 1)
};
For more information on comparing strings, check out this article: How to compare strings in C#.
Another approach would be to implement an extension method for the string class that contains this functionality, for example:
public static class Extensions
{
public static bool IsInRange(this string value, string start, string end,
bool ignoreCase = false)
{
return string.Compare(value, start, ignoreCase) > -1 &&
string.Compare(value, end, ignoreCase) < 1;
}
}
This would help reduce the amount of code you need to write in your Linq statement:
var personCount = from r in range
select new
{
r.ID,
NumberOfPersonsInRange = person.Count(p =>
p.Surname.IsInRange(r.StartRange, r.EndRange, true))
};
In C#, given the array :
string[] myStrings = new string[] {
"test#test",
"##test",
"######", // Winner (outputs 6)
};
How can I find the maximum number of occurrences that the character # appears in a single string ?
My current solution is :
int maxOccurrences = 0;
foreach (var myString in myStrings)
{
var occurrences = myString.Count(x => x == '#');
if (occurrences > maxOccurrences)
{
maxOccurrences = occurrences;
}
}
return maxOccurrences;
Is their a simplier way using linq that can act directly on the myStrings[] array ?
And can this be made into an extension method that can work on any IEnumerable<string> ?
First of all let's project your strings into a sequence with count of matches:
myStrings.Select(x => x.Count(x => x == '#')) // {1, 2, 6} in your example
Then pick maximum value:
int maximum = myStrings
.Select(s => s.Count(x => x == '#'))
.Max(); // 6 in your example
Let's make an extension method:
public static int CountMaximumOccurrencesOf(this IEnumerable<string> strings, char ch)
{
return strings
.Select(s => s.Count(c => c == ch))
.Max();
}
However there is a big HOWEVER. What in C# you call char is not what you call character in your language. This has been widely discussed in other posts, for example: Fastest way to split a huge text into smaller chunks and How can I perform a Unicode aware character by character comparison? then I won't repeat everything here. To be "Unicode aware" you need to make your code more complicate (please note code is wrote here then it's untested):
private static IEnumerable<string> EnumerateCharacters(string s)
{
var enumerator = StringInfo.GetTextElementEnumerator(s.Normalize());
while (enumerator.MoveNext())
yield return (string)enumerator.Value;
}
Then change our original code to:
public static int CountMaximumOccurrencesOf(this IEnumerable<string> strings, string character)
{
return strings
.Select(s => s.EnumerateCharacters().Count(c => String.Equals(c, character, StringComparison.CurrentCulture))
.Max();
}
Note that Max() alone requires collection to don't be empty (use DefaultIfEmpty() if collection may be empty and it's not an error). To do not arbitrary decide what to do in this situation (throw an exception if it should happen or just return 0) you can may make this method less specialized and leave this responsibility to caller:
public static int CountOccurrencesOf(this IEnumerable<string> strings,
string character,
StringComparison comparison = StringComparison.CurrentCulture)
{
Debug.Assert(character.EnumerateCharacters().Count() == 1);
return strings
.Select(s => s.EnumerateCharacters().Count(c => String.Equals(c, character, comparison ));
}
Used like this:
var maximum = myStrings.CountOccurrencesOf("#").Max();
If you need it case-insensitive:
var maximum = myStrings.CountOccurrencesOf("à", StringComparison.CurrentCultureIgnoreCase)
.Max();
As you can now imagine this comparison isn't limited to some esoteric languages but it also applies to invariant culture (en-US) then for strings that must always be compared with invariant culture you should specify StringComparison.InvariantCulture. Don't forget that you may need to call String.Normalize() also for input character.
You can write something like this. Note the usage of DefaultIfEmpty, to not throw an exception if myStrings is empty, but revert to 0.
var maximum = myStrings.Select(e => e.Count(ee => ee == '#')).DefaultIfEmpty().Max();
You can do that with Linq combined to Regex:
myStrings.Select(x => Regex.Matches(x, "#").Count).max();
let assume we have an array like this:
var caps = new[] { "1512x", "001xx", "27058", "201xx", "4756x" };
(original array is huge and come from another linq query)
What I need is to create a LINQ statement accepting a value and tries to match with one of the values on the foreseen array.
For example if I use "15121" I need to match the "1512x" value on the array and return it.
Obviously, if I use "27058" it finds the exact match and simply return it.
Is it possible in LINQ?
The "wildcard" char on the array is "x", but I can change it.
Thanks in advance!
Valerio
You can use regular expressions:
var value = "15121";
var caps = new[] { "1512x", "001xx", "27058", "201xx", "4756x" };
var match = caps
.FirstOrDefault(c => new Regex("^" + c.Replace("x", "[0-9]") + "$").IsMatch(value));
if (match != null)
Console.WriteLine("{0} matches {1}", value, match);
The "pattern" 001xx is converted into the regular expression ^001[0-9][0-9]$ and so on. Then the first matching regular expressions is found.
But if the caps is huge it might not perform so well because each regular expression has to be compiled and converted into a state machine until a match is found.
Assuming you have a predicate method, something like this (or something equivalent using Regex, as described in another answer):
static bool Match(string pattern, string exact)
{
if(pattern.Length != exact.Length) return false;
for(var i = 0; i < pattern.Length; i++)
if(pattern[i] != exact[i] && pattern[i] != 'x')
return false;
return true;
}
Then the LINQ query can look like this:
var found = caps.Single(x => Match(x, yourSearch));
I was wondering if it is possible to write an expression for a Linq extension (or a custom extension) to filter a collection using a lambda expression that compares two elements of the collection.
In other words, if I have a List<DateTime> and some value, var v = DateTime.Today, then I am wondering if it is possible to write create a method that will return the first element of the collection that is less than or equal to the value, current <= v, with the next element of the collection being greater than or equal to the value, next >= v.
Please note that the above is just an example, and may or may not be the final implementation.
The following would be a valid solution, were the .First() method to accept Func<DateTime, DateTime, bool> with the two DateTime parameters being consecutive elements of the sequence:
dateCollection.First((current, next) => current <= v && next >= v);
Please also note that with the example given, a valid workaround could be to use .OrderBy and then find the first index that is greater than d and subtract 1. However, this type of comparison is not the only one that I am looking for. I may have a situation in which I am checking a List<string> for the first situation where the current element starts with the first letter of my value, v, and the next element ends with the last letter of my value, v.
I am looking for something that would be just a few of code. My goal is to find the simplest solution to this possible, so brevity carries a lot of weight.
What I am looking for is something of the form:
public static T First (...)
{
...
}
I believe that this will also require two or more lambda expressions as parameters. One thing that may also provide a good solution is to be able to select into all possible, consecutive pairs of elements of the sequence, and call the .First() method on that.
For example:
//value
var v = 5;
//if my collection is the following
List<int> stuff = { a, b, c, d };
//select into consecutive pairs, giving:
var pairs = ... // { { a, b }, { b, c }, { c, d } };
//then run comparison
pairs.First(p => p[0] <= v && p[1] >= v).Select(p => p[0]);
Thanks and happy coding! :)
What we can create is a Pairwise method that can map a sequence of values into a sequence of pairs representing each value and the value that comes before it.
public static IEnumerable<Tuple<T, T>> Pairwise<T>(this IEnumerable<T> source)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
yield break;
T prev = iterator.Current;
while (iterator.MoveNext())
{
yield return Tuple.Create(prev, iterator.Current);
prev = iterator.Current;
}
}
}
Now we can write out:
var item = data.Pairwise()
.First(pair => pair.Item1 <= v && pair.Item2 >= v)
.Item1;
If this is something you're going to use a fair bit, it may be worth creating a new custom type to replace Tuple, so that you can have Current and Next properties, instead of Item1 and Item2.
List<int> list = new List<int>();
list.Add(3);
list.Add(2);
list.Add(8);
list.Add(1);
list.Add(4);
var element = list
.Where((elem, idx) => idx < list.Count-1 && elem<=list[idx+1])
.FirstOrDefault();
Console.WriteLine(element);
rsult: 2
where 'elem' is the current element, and 'idx' is an index of the current element
Not really sure what you want to return here is my main question, but to take your example and put it into a LINQ statement, it would be like this:
DateTime? Firstmatch = dateCollection.DefaultIfEmpty(null).FirstOrDefault(a => a <= d && ((dateCollection.IndexOf(a) + 1) < (dateCollection.Count) && dateCollection[dateCollection.IndexOf(a) + 1] >= d));
Strictly following the description, you could combine linq and list indexes to find the first index that matches your criterium, and returning its element:
DateTime d= DateTime.Today;
var res = dateCollection[Enumerable.Range(0, dateCollection.Count - 1).First(i => dateCollection[i] <= d && dateCollection[i + 1] >= d)];
Servy's answer can be handled without an extension method:
var first = items
.Select((current, index) => index > 0 ? new { Prev = items.ElementAt(index-1), Current = current } : null)
.First(pair => pair != null && pair.Prev <= v && pair.Current >= v)
.Prev;
I am trying to read in a file which is essentially a list of integers, seperated by a line. Obviously, file input can never be trusted so I need to filter out non-integers.
1
2
3
4
I know the as operator usually converts if it can and then assigns a null, however because int isn't nullable this isn't the case. I thought that perhaps I could cast to Nullable<int>. I have never really delved into this, I thought perhaps I could do:
var lines = File.ReadAllLines("");
var numbers = lines.Select(line => line as int?).Where(i => i != null);
I know that I could get potentially get around this by doing:
var numbers = lines.Select(line =>
{
int iReturn = 0;
if (int.TryParse(line, out iReturn ))
return iReturn;
else
return null;
}).Where(i => i != null);
I also could potentially do it as an extension method.
I was just looking for a neat, concise way of doing the cast in a statement and also understanding why my code is invalid.
I'm always using this simple extension method:
public static int? TryGetInt(this string item)
{
int i;
bool success = int.TryParse(item, out i);
return success ? (int?)i : (int?)null;
}
Then it's easy:
var numbers = lines.Select(line => line.TryGetInt())
.Where(i => i.HasValue)
.Select(i => i.Value);
You can also use int.TryParse without the extension, but that is undocumented hence might stop working in future:
int i = 0;
var numbers = lines.Where(line => int.TryParse(line, out i))
.Select(line => i);
Edit
"also understanding why my code is invalid"
relevant code:
if (int.TryParse(line, out iReturn ))
return iReturn;
else
return null;
It would work if you'd replace
else
return null;
with
else
return (int?)null;
because you are returning an int, but null is not convertible implicitly to an int.
There isn't a concise way to do this because here you don't need to cast (you cannot cast) -- you need to convert from one type to another. The types of course are int and string (so not exactly "any" types), but as in the general case any conversion between unrelated types cannot be done "just like that".
Nope. C# is deliberately cautious about changing strings to numbers.
You can make your code shorter (no more nulls) using a foreach loop
var numbers = new List<int>();
foreach(string line in lines)
{
int n;
if (int.TryParse(line, out n))
numbers.Add(n);
}
If I understand you correctly and you want just filter the non integer lines, maybe regex is an option?
var lines = File.ReadAllLines("");
var numbers = lines.Where(i => Regex.IsMatch(i, "[0-9]+"));
Here's the best I came up with:
Use this extension method:
public static class Int32Extensions
{
public static int? ParseOrDefault(this string text)
{
int iReturn = 0;
if (int.TryParse(text, out iReturn))
{
return iReturn;
}
return null;
}
}
Like this:
var query = lines.Select(x => x.ParseOrDefault()).Where(x => x.HasValue);
You can create and extension method
public static int? ToInt(this string s, int default){ ... }
and use it in LINQ:
var lines = File.ReadAllLines(path);
var numbers = lines.Select(line => line.ToInt())
.Where(i => i != null);