IQueryable cannot translate numeric string comparer - c#

I'm trying to sort the data by the type "V0003", "4323Fw" but an error occurs with comparer, what is going wrong and how can I sort in IQueryable?
My comparer:
public class NumericStringComparer : IComparer<string>
{
public int Compare(string s1, string s2)
{
const int S1GreaterThanS2 = 1;
const int S2GreaterThanS1 = -1;
const int S2EqualsS1 = 0;
var isS1Numeric = IsNumeric(s1);
var isS2Numeric = IsNumeric(s2);
if (isS1Numeric && isS2Numeric)
{
var firstNum = Convert.ToInt64(s1);
var secondNum = Convert.ToInt64(s2);
if (firstNum > secondNum)
{
return S1GreaterThanS2;
}
if (firstNum < secondNum)
{
return S2GreaterThanS1;
}
return S2EqualsS1;
}
if (isS1Numeric)
{
return S2GreaterThanS1;
}
if (isS2Numeric)
{
return S1GreaterThanS2;
}
return string.Compare(s1, s2, true, CultureInfo.InvariantCulture);
static bool IsNumeric(string value)
{
return long.TryParse(value, out _);
}
}
}
My sort:
query.OrderBy(bdc => bdc.Code, comparer), // query is IQueryable<MyClass>
Error:
"The LINQ expression""DbSet()\r\n .Where(bdc => bdc.DateEnd >= DateTime.UtcNow && bdc.Deleted == False)\r\n .OrderByDescending(bdc => bdc.Id)\r\n .Include(bdc => bdc.InverseParent)\r\n .ThenInclude(bdc => bdc.InverseParent)\r\n .ThenInclude(bdc => bdc.InverseParent)\r\n .Where(bdc => (int)bdc.StagesBudgetCycle == (int)(short)__StagesBudgetCycle_0 || bdc.InverseParent\r\n .AsQueryable()\r\n .Any(children => (int)children.StagesBudgetCycle == (int)(short)__StagesBudgetCycle_0 || children.InverseParent\r\n .AsQueryable()\r\n .Any(children => (int)children.StagesBudgetCycle == (int)(short)__StagesBudgetCycle_0 || children.InverseParent\r\n .AsQueryable()\r\n .Any(children => (int)children.StagesBudgetCycle == (int)(short)__StagesBudgetCycle_0))))\r\n .OrderBy(\r\n keySelector: bdc => bdc.Code, \r\n comparer: __p_1)""could not be translated. Either rewrite the query in a form that can be translated",
"or switch to client evaluation explicitly by inserting a call to""AsEnumerable",
"AsAsyncEnumerable",
"ToList",
"or""ToListAsync"". See https"://go.microsoft.com/fwlink/?linkid=2101038 for more information.

Related

How to fix : Cannot convert lambda expression to type int because is not delegate?

i am trying to get user decision from the list but i get that Cannot convert lambda expression to type int because is not delegate. How can i solve this? I have had a look on the internet but since i am quite new i am not sure what is the right solution
public static List<int> UserDecisionResult { get; set; }
public void GetUserDecision()
{
List<int> userDecision = new List<int>();
if (FilterAllItems) userDecision.Add(_parentCategoryId = -1);
if (FilterBeginnerItems) userDecision.Add(_parentCategoryId = 1);
if (FilterIntermediateItems) userDecision.Add(_parentCategoryId = 2);
if (FilterUpperIntermediateItems) userDecision.Add(_parentCategoryId = 3);
if (FilterAdvancedItems) userDecision.Add(_parentCategoryId = 4);
UserDecisionResult = userDecision;
}
private static List<Article> FindAllArticlesForPurchase(List<Article> allArticles)
{
var result = UserDecisionResult;
if (_parentCategoryId != -1)
{
foreach (var categoryGroup in _allUserCategoryGroups)
{
var allGroupCategories = _allCategories.Where(m => m.CategoryGroupId == categoryGroup.Id).ToList();
if (_parentCategoryId != -1)
{
foreach (var category in allGroupCategories)
{
if (category.ParentId == _parentCategoryId && _parentCategoryId != -1)
{
var categoryArticles = _allArticlesForPurchase.Where(result.Contains(m => m.CategoryId == category.Id).ToList();
//var categoryArticles = _allArticlesForPurchase.Where(result.Contains(m => m.CategoryId == category.Id).ToList());
allArticles.AddRange(categoryArticles);
}
}
}
}
}
else
{
allArticles = _allArticlesForPurchase;
}
return allArticles;
}
Your lambda expression is wrong here due to syntax. You can try:
var categoryArticles = _allArticlesForPurchase
.Where(m => m.CategoryId == category.Id
&& result.Contains(m.CategoryId))
.ToList();
or if Contains here is some custom implementation then try :
var categoryArticles = _allArticlesForPurchase
.Where(_ => result.Contains(m => _.CategoryId == category.Id))
.ToList();

Icomparer c# List

I have a list of image name like this {"1.jpg", "10.jpg", "2.jpg"}.
I would like to sort like this {"1.jpg", "2.jpg", "10.jpg"}.
I created this comparer. That means if x or y == "DSC_10.jpg", so if list is {"DSC_1.jpg", "DSC_10.jpg", "DSC_2.jpg", ...} don't sort and keep the list.
var comparer = new CompareImageName();
imageUrls.Sort(comparer);
return imageUrls;
public class CompareImageName : IComparer<string>
{
public int Compare(string x, string y)
{
if (x == null || y == null) return 0;
var l = x.Split('/');
var l1 = y.Split('/');
int a, b;
var rs = int.TryParse(l[l.Length - 1].Split('.')[0], out a);
var rs2 = int.TryParse(l1[l1.Length - 1].Split('.')[0], out b);
if (!rs || !rs2) return 0;
if (a == b || a == 0 && b == 0) return 0;
return a > b ? 1 : -1;
}
}
This sort correctly with name {"1.jpg", "10.jpg", "2.jpg"}, but incorrectly if list is {"DSC_1.jpg", "DSC_10.jpg", "DSC_2.jpg", ...}.
I read in MSDN:
What wrong with my code?
I think you're better off doing a bit of Regex for this. Try this solution:
public class CompareImageName : IComparer<string>
{
public int Compare(string x, string y)
{
if (x == null || y == null) return 0;
var regex = new Regex(#"/(((?<prefix>\w*)_)|)((?<number>\d+))\.jpg$");
var mx = regex.Match(x);
var my = regex.Match(y);
var r = mx.Groups["prefix"].Value.CompareTo(my.Groups["prefix"].Value);
if (r == 0)
{
r = int.Parse(mx.Groups["number"].Value).CompareTo(int.Parse(my.Groups["number"].Value));
}
return r;
}
}
Apart from the Regex string itself this is easier to follow the logic.
Here is your solution check this example, following class will do the comparison
public class NumericCompare : IComparer<string>
{
public int Compare(string x, string y)
{
int input1,input2;
input1=int.Parse(x.Substring(x.IndexOf('_')+1).Split('.')[0]);
input2= int.Parse(y.Substring(y.IndexOf('_')+1).Split('.')[0]);
return Comparer<int>.Default.Compare(input1,input2);
}
}
You can make use of this class like the following:
var imageUrls = new List<string>() { "DSC_1.jpg", "DSC_10.jpg", "DSC_2.jpg" };
var comparer = new NumericCompare();
imageUrls.Sort(comparer);
Console.WriteLine(String.Join("\n",imageUrls));
Try this with simple OrderBy
var SortedList = imageUrls.OrderBy(
x=>int.Parse(
x.Substring(x.IndexOf('_')+1).Split('.')[0])
).ToList();
Basically what you want to do is sort by the numeric part within the string. You are almost there. You just have to handle the part when you split a case like this DSC_2.jpg using a . then the first part is not all digits. So you need to get digits and then compare those. Here is the code. Please note I have made the assumption you will have backslash and if that is not the case then please handle it:
public int Compare(string x, string y)
{
if (x == null || y == null) return 0;
var nameX = x.Substring(x.LastIndexOf('/'));
var nameY = y.Substring(y.LastIndexOf('/'));
var nameXParts = nameX.Split('.');
var nameYParts = nameY.Split('.');
int a, b;
var rs = int.TryParse(nameXParts[0], out a);
var rs2 = int.TryParse(nameYParts[0], out b);
var nameXDigits = string.Empty;
if (!rs)
{
for (int i = 0; i < nameXParts[0].Length; i++)
{
if (Char.IsDigit(nameXParts[0][i]))
nameXDigits += nameXParts[0][i];
}
}
var nameYDigits = string.Empty;
if (!rs2)
{
for (int i = 0; i < nameYParts[0].Length; i++)
{
if (Char.IsDigit(nameYParts[0][i]))
nameYDigits += nameYParts[0][i];
}
}
int.TryParse(nameXDigits, out a);
int.TryParse(nameYDigits, out b);
if (a == b || a == 0 && b == 0) return 0;
return a > b ? 1 : -1;
}
Don't use imageUrls.Sort(comparer); on List because it doesn't accept 0 value as keeping the order of elements.
Reason:
The Sort performs an unstable sort; that is, if two elements are equal, their order might not be preserved. In contrast, a stable sort preserves the order of elements that are equal.
Link: https://msdn.microsoft.com/en-gb/library/w56d4y5z.aspx
Solution: Let's try to use OrderBy with your compare
var imageUrls1 = new List<string>() { "1.jpg", "10.jpg", "2.jpg" };
var imageUrls2 = new List<string>() { "DSC_1.jpg", "DSC_10.jpg", "DSC_2.jpg" };
var comparer = new CompareImageName();
//Sort normally
imageUrls1 = imageUrls1.OrderBy(p=>p, comparer).ToList();
//Keep the order as your expectation
imageUrls2 = imageUrls2.OrderBy(p=>p, comparer).ToList();
Maybe you can try doing this in a function instead of writing a comparator. I can't think of a good way to implement this logic as a comparator since there are different rules based on the contents (don't sort if the file name is not numeric).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace sortinglists
{
public class MainProgram
{
public static void Main()
{
var imageUrlsNumbers = new List<string>();
imageUrlsNumbers.Add("c:/a/b/1.jpg");
imageUrlsNumbers.Add("c:/a/b/10.jpg");
imageUrlsNumbers.Add("c:/a/b/2.jpg");
CustomSort(ref imageUrlsNumbers);
foreach (var imageUrl in imageUrlsNumbers)
{
Console.WriteLine(imageUrl);
}
var imageUrlsText = new List<string>();
imageUrlsText.Add("c:/a/b/DSC_1.jpg");
imageUrlsText.Add("c:/a/b/DSC_10.jpg");
imageUrlsText.Add("c:/a/b/DSC_2.jpg");
CustomSort(ref imageUrlsText);
foreach (var imageUrl in imageUrlsText)
{
Console.WriteLine(imageUrl);
}
}
public static void CustomSort(ref List<string> imageUrls)
{
if (imageUrls
.Select(s => s.Substring(s.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1))
.Select(t => t.Substring(0, t.IndexOf(".", StringComparison.OrdinalIgnoreCase)))
.Where(u => new Regex("[A-Za-z_]").Match(u).Success)
.Any())
{
imageUrls = imageUrls
.Select(x => x.Substring(x.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1))
.ToList();
}
else
{
imageUrls = imageUrls
.Select(v => v.Substring(v.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1))
.OrderBy(w => Convert.ToInt32(w.Substring(0, w.LastIndexOf(".", StringComparison.OrdinalIgnoreCase))))
.ToList();
}
}
}
}
The output for imageUrlsNumbers after sorting is:
1.jpg
2.jpg
10.jpg
And the output for imageUrlsText after sorting is:
DSC_1.jpg
DSC_10.jpg
DSC_2.jpg

sort number and alphabet string using EF

I have a column with `string type in my database that contains these value :
410
AFP-EXEC
412
411
AFP-EXEP
So i want to sort them as you can see :
_materialIssueVoucherRepository.Get().OrderBy(i=>int.Parse(i.Code)).ToList()
But it returns an obvious error Convert error string to int,The result should be like this :
410
411
412
AFP-EXEC
AFP-EXEP
The Alphabet part is not important,Can i do that in EF?
int temp;
_materialIssueVoucherRepository
.Get()
.OrderBy(i => int.TryParse(i.Code, out temp) ? temp : int.MaxValue)
.ToList()
it sounds like you want to ignore the non-numeric characters in the string and then use the integers as part of the sorting. You can try:
.OrderBy(i => int.Parse(new string(i.Where(char.IsDigit).ToArray()))
which will grab only the integers from the string for a comparison, though it's not very pretty.
You need to use a custom comparer for this purpose. Like this:
public class AlphanumComparator : IComparer<string>
{
public int Compare(string str1, string str2)
{
if (IsNumeric(str1) && IsNumeric(str2))
{
if (Convert.ToInt32(str1) > Convert.ToInt32(str2)) return 1;
if (Convert.ToInt32(str1) < Convert.ToInt32(str2)) return -1;
if (Convert.ToInt32(str1) == Convert.ToInt32(str2)) return 0;
}
if (IsNumeric(str1) && !IsNumeric(str2))
return -1;
if (!IsNumeric(str1) && IsNumeric(str2))
return 1;
return String.Compare(str1, str2, StringComparison.OrdinalIgnoreCase);
}
public static bool IsNumeric(object value)
{
try
{
int num = Convert.ToInt32(value.ToString());
return true;
}
catch (FormatException)
{
return false;
}
}
}
Then:
_materialIssueVoucherRepository.Get().OrderBy(x => x.Code, new AlphanumComparator()).ToList();

How to write a function that generates ID by taking missing items in a sequence?

How can I write an algorithm that can take unused ID's out of a sequence starting from 1 to 99 in the format "C00"? For example NewId(['C01', 'C02', 'C03']) should emit 'C04', but NewId(['C02', 'C03', 'C04']) should emit C01, and NewId(['C01', 'C03', 'C04']) should result in C02.
I wrote an implementation but the result is wrong.
Example : CAT_ID : C01, C02, C05, C06, C11. When I run it, the expected result is C03. My algorithm is as follows:
Sort ID asc
Go through every item in the list
Compare first value with the next, if they are not the same, add 1 and exit loop.
This is my code:
public static string Get_AreaID_Auto()
{
string result = "";
if (db.TESTs.ToList().Count <= 0)
{
result = "01";
}
else
{
int maxId = 0;
foreach (var item in db.TESTs.OrderBy(e => e.CAT_ID).ToList())
{
if (int.Parse(item.CAT_ID.Substring(1)) + 1 != int.Parse(item.CAT_ID.Substring(1)))
{
maxId = int.Parse(item.CAT_ID.Substring(1) + 1);
break;
}
}
switch (maxId.ToString().Length)
{
case 1:
if (maxId == 9)
{
result = "10";
}
else
result = "0" + (maxId + 1);
break;
case 2:
result = "" + (maxId + 1);
break;
default:
break;
}
}
return "C" + result;
}
Can someone point out what is wrong?
This should work for you:
public static string Get_AreaID_Auto()
{
var existing = db.TESTs.Select(e => e.CAT_ID).OrderBy(x => x).ToList();
if (existing.Count == 0)
{
return "C01";
}
else
{
return
existing
.Concat(new [] { "" })
.Select((x, n) => new
{
actual = x,
expected = String.Format("C{0:00}", n + 1),
})
.Where(x => x.actual != x.expected)
.Select(x => x.expected)
.First();
}
}
This uses a generate and test approach. No parsing necessary.
I just realised with the .Concat(new [] { "" }) change that the if statement is now no longer required. You can do this instead:
public static string Get_AreaID_Auto()
{
return
db.TESTs
.Select(e => e.CAT_ID)
.OrderBy(x => x)
.ToArray()
.Concat(new [] { "" })
.Select((x, n) => new
{
actual = x,
expected = String.Format("C{0:00}", n + 1),
})
.Where(x => x.actual != x.expected)
.Select(x => x.expected)
.First();
}
Try this
public static string Get_AreaID_Auto()
{
string result = "";
if (db.TESTs.ToList().Count <= 0)
{
result = "01";
}
else
{
var item = db.TESTs.OrderByDescending(e => e.CAT_ID).First();
result = int.Parse(item.CAT_ID.Substring(1)) + 1;
}
return string.Format("C{0:D3}",result);
}
Updated Code...Now Try this
public static string Get_AreaID_Auto()
{
string result = "";
if (db.TESTs.ToList().Count <= 0)
{
result = "01";
}
else
{
var items = db.TESTs.OrderBy(e => e.CAT_ID).ToArray();
for(int i=0;i<items.count;i++)
{
if ((i==items.count-1) || (int.Parse(items[i].CAT_ID.Substring(1)) + 1 != int.Parse(items[i+1].CAT_ID.Substring(1))))
{
result = int.Parse(items[i].CAT_ID.Substring(1) + 1);
break;
}
}
}
return string.Format("C{0:D2}",result);
}
Here is a solution I think would work:
var items = db.TESTs.Select(x => int.Parse(x.CAT_ID.Substring(1))).OrderBy(v => v).ToArray();
if(!items.Any())
return "C01";
int current = 0;
for (int i = 0; i < items.Length; i++)
{
if (items[i] > current + 1)
return "C" + (current + 1) .ToString("00");
current = items[i];
}
return "C" + (items.Max() + 1).ToString("00");

Using LinqToSql without load base in memory

I have one problem. If i'm using LinqToSql, my program load my database in memory.
little example:
//pageNumber = 1; pageSize = 100;
var result =
(
from a in db.Stats.AsEnumerable()
where (DictionaryFilter(a, sourceDictionary) && DateFilter(a, beginTime, endTime) && ErrorFilter(a, WarnLevel))
select a
);
var size = result.Count(); // size = 1007
var resultList = result.Skip((pageNumber-1)*pageSize).Take(pageSize).ToList();
return resultList;
DictionaryFilter, DateFilter and ErrorFilter are functions that filter my datebase.
after this my program use ~250Mb of Ram.
if i dont use:
var size = result.Count();
My program use ~120MB Ram.
Before use this code, my program use ~35MB Ram.
How can I use count and take functions not loading all my datebase in memory?
static bool DateFilter(Stat table, DateTime begin, DateTime end)
{
if ((table.RecordTime >= begin.ToFileTime()) && (table.RecordTime <= end.ToFileTime()))
{
return true;
}
return false;
}
static bool ErrorFilter(Stat table, bool[] WarnLevel)
{
if (WarnLevel[table.WarnLevel]) return true;
else return false;
}
static bool DictionaryFilter(Stat table, Dictionary<GetSourcesNameResult, bool> sourceDictionary)
{
foreach (var word in sourceDictionary)
{
if (table.SourceName == word.Key.SourceName)
{
return word.Value;
}
}
//
return false;
}
Simple: don't use .AsEnumerable(). That means "switch to LINQ-to-Objects". Before that, db.Stats was IQueryable<T>, which is a composable API, and would do what you expect.
That, however, means that you can't use C# methods like DictionaryFilter and DateFilter, and must instead compose things in terms of the Expression API. If you can illustrate what they do I can probably advise further.
With your edit, the filtering can be tweaked, for example:
static IQueryable<Stat> ErrorFilter(IQueryable<Stat> source, bool[] WarnLevel) {
// extract the enabled indices (match to values)
int[] levels = WarnLevel.Select((val, index) => new { val, index })
.Where(pair => pair.val)
.Select(pair => pair.index).ToArray();
switch(levels.Length)
{
case 0:
return source.Where(x => false);
case 1:
int level = levels[0];
return source.Where(x => x.WarnLevel == level);
case 2:
int level0 = levels[0], level1 = levels[1];
return source.Where(
x => x.WarnLevel == level0 || x.WarnLevel == level1);
default:
return source.Where(x => levels.Contains(x.WarnLevel));
}
}
the date filter is simpler:
static IQueryable<Stat> DateFilter(IQueryable<Stat> source,
DateTime begin, DateTime end)
{
var from = begin.ToFileTime(), to = end.ToFileTime();
return source.Where(table => table.RecordTime >= from
&& table.RecordTime <= to);
}
and the dictionary is a bit like the levels:
static IQueryable<Stat> DictionaryFilter(IQueryable<Stat> source,
Dictionary<GetSourcesNameResult, bool> sourceDictionary)
{
var words = (from word in sourceDictionary
where word.Value
select word.Key.SourceName).ToArray();
switch (words.Length)
{
case 0:
return source.Where(x => false);
case 1:
string word = words[0];
return source.Where(x => x.SourceName == word);
case 2:
string word0 = words[0], word1 = words[1];
return source.Where(
x => x.SourceName == word0 || x.SourceName == word1);
default:
return source.Where(x => words.Contains(x.SourceName));
}
}
and:
IQueryable<Stat> result = db.Stats;
result = ErrorFilter(result, WarnLevel);
result = DateFiter(result, beginTime, endTime);
result = DictionaryFilter(result, sourceDictionary);
// etc - note we're *composing* a filter here
var size = result.Count(); // size = 1007
var resultList = result.Skip((pageNumber-1)*pageSize).Take(pageSize).ToList();
return resultList;
The point is we're now using IQueryable<T> and Expression exclusively.
The following SO question might explain things: Understanding .AsEnumerable in Linq To Sql
.AsEnumerable() loads the entire table.

Categories