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();
Related
This is my attempt to code the ToString method but I'm not sure how to return all of this?
I want to use a StringBuilder object and append all active items to it including their key value and the array index.
Below is the code
public override string ToString()
{
StringBuilder s = new StringBuilder();
for (int i = 0; i < mTable.Length; i++)
{
if (mTable[i].Status == EntryType.Active)
{
s.Append(mTable[i].Value);
s.Append(mTable[i]);
}
Console.WriteLine(mTable[i]);
Console.WriteLine(mTable[i].Key);
}
//return "position" + mTable[].Value;
}
//example of output
//TableIndex KEY VALUE
//0 NULL NULL
//1 NULL NULL
//2 100 256
//3 101 852
//4 NULL NULL
//5 102 706
//6 NULL NULL
You're most of the way there. You shouldn't do any output in the ToString() method itself. The ToString() method just return a string with no side effects.
Without seeing the mTable definition and the definition of the objects stored in the mTable, this is the best I can really provide.
public override string ToString()
{
StringBuilder s = new StringBuilder();
for (int i = 0;i < mTable.Length; i++)
{
if (mTable[i].Status == EntryType.Active)
{
// Use s.Append() or s.AppendFormat() to build up your string
}
}
return s.ToString();
}
Don't reinvent the wheel ... Use
return String.Concat(
mTable.Where(w=>w.Status == EntryType.Active)
.Select(s=>$"{s.Key}:{s.Value};")
)
for fancy outputs:
public override string ToString()
{
return String.Join(Environment.NewLine,
mTable.Select((s,i)=>new {Index=i, s.Key, s.Value, s.Status})
.Where(w => w.Status == EntryType.Active)
.Select(s => $"[{s.Index}]Key:{s.Key}=Value:{s.Value}")
);
}
Done. Curious, no separators?
There is a function in .NET
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index);
Is there a simply way to use similar function to find out if substrings occurs at some position (index)?
I mean:
public static bool ElementAtPosContains(this string, int index, string[] valuesToCheck)
{ ... }
string test1 = "abcd5f";
string[] substrings = {"1" , "2", "3", "4", "5"};
if (test.ElementAtPosContains(4, substrings))
{
DoSomething();
}
If there would be 1, 2, 3, 4, 5 at 4 position in string - return true. I can do this thing:
public static bool ElementAtPosContains(this string inputStr, int index, string[] valuesToCheck)
{
if (valuesToCheck == null)
return false;
foreach (string value in valuesToCheck)
{
if (inputStr.Substring(index, value.Length) == value)
return true;
}
return false;
}
But this seems to be not very effective
Try with this:
public static bool ElementAtPosContains(this string inputStr, int index, string[] valuesToCheck)
{
if (valuesToCheck == null || valuesToCheck.Length == 0 || inputStr.Length < index)
return false;
return valuesToCheck.Any(sub => string.CompareOrdinal(inputStr, index, sub, 0, sub.Length) == 0);
}
CompareOrdinal comparisons are always case-sensitive and don't take culture into consideration. This makes them faster then usual Compare or = operator. If you don't need culture-aware comparison or case-insensitive comparison, this method should do the job faster since it compares numeric values of characters from the string.
Demo
This seems to be smarter:
public static bool ElementAtPosContains(this string inputStr, int index, IList<String> valuesToCheck)
{
if (valuesToCheck == null || inputStr.Length < index)
return false;
return valuesToCheck.Any(s => s.Length + index <= inputStr.Length
&& inputStr.IndexOf(s, index) == index);
}
Demo
Although i think that you're doing premature optimization.
I have a dictionary containg ID which are alphanumeric (e.g. a10a10 & d10a9) from which I want the biggest ID, meaning 9 < 10 < a ...
When I use the following code, d10a9 is MAX since 9 is sorted before 10
var lsd = new Dictionary<string, string>();
lsd.Add("a", "d10a10");
lsd.Add("b", "d10a9");
string max = lsd.Max(kvp => kvp.Value);
How can I get the Max value of the IDs with the Longest string combined?
I think you may try to roll your own IComparer<string>
class HumanSortComparer : IComparer<string>
{
public int Compare(string x, string y)
{
// your human sorting logic here
}
}
Usage:
var last = collection.OrderBy(x => x.Value, new HumanSortComparer()).LastOrDefault();
if (last != null)
string max = last.Value;
this works like a charm assuming IDs always start with "d10a":
int max = lsd.Max(kvp => Convert.ToInt32(kvp.Value.Substring(4)));
Console.Write(string.Format("d10a{0}", max));
One way would be to do this
string max =lsd.Where(kvp=>kvp.Value.Length==lsd.Max(k=>k.Value.Length)).Max(kvp => kvp.Value);
however I think that this method would evalute the max length for each item so you may be better to extract it to a variable first
int maxLength=lsd.Max(kvp=>kvp.Value.Length);
string max = lsd.Where(kvp=>kvp.Value.Length == maxLength).Max(kvp => kvp.Value);
If you are going to have null strings in there you may need to perform null checks too
int maxLength=lsd.Max(kvp=>(kvp.Value??String.Empty).Length);
string max = lsd.Where(kvp=>(kvp.Value??String.Empty).Length == maxLength).Max(kvp => kvp.Value);
Alternatively treat your string as Base36 number and convert to long for the max function and then convert back again to get the max string.
string max =lsd.Max(tvp=>tvp.Value.FromBase36()).ToBase36();
public static class Base36 {
public static long FromBase36(this string src) {
return src.ToLower().Select(x=>(int)x<58 ? x-48 : x-87).Aggregate(0L,(s,x)=>s*36+x);
}
public static string ToBase36(this long src) {
StringBuilder result=new StringBuilder();
while(src>0) {
var digit=(int)(src % 36);
digit=(digit<10) ? digit+48 :digit+87;
result.Insert(0,(char)digit);
src=src / 36;
}
return result.ToString();
}
}
Finally just just the Agregate extension method instead of Max as this lets you do all the comparison logic....
lsd.Agregate(string.Empty,(a,b)=> a.Length == b.Length ? (a>b ? a:b) : (a.Length>b.Length ? a:b));
This could doesn't have null checks but you easily add them in.
I think if you did this:
var max = lsd.OrderByDescending(x => x.Value)
.GroupBy(x => x.Value.Length)
.OrderByDescending(x => x.Key)
.SelectMany(x => x)
.FirstOrDefault();
It may give you what you want.
You need StringComparer.OrdinalIgnoreCase.
Without the need to use linq, the function that do that is quite simple.
Complexity is, of course, O(n).
public static KeyValuePair<string, string> FindMax(IEnumerable<KeyValuePair<string, string>> lsd)
{
var comparer = StringComparer.OrdinalIgnoreCase;
var best = default(KeyValuePair<string, string>);
bool isFirst = true;
foreach (KeyValuePair<string, string> kvp in lsd)
{
if (isFirst || comparer.Compare(kvp.Value, best.Value) > 0)
{
isFirst = false;
best = kvp;
}
}
return best;
}
Okay - I think you need to first turn each key into a series of strings and numbers - since you need the whole number to be able to determine the comparison. Then you implement an IComparer - I've tested this with your two input strings as well as with a few others and it appears to do what you want. The performance could possibly be improved - but I was brainstorming it!
Create this class:
public class ValueChain
{
public readonly IEnumerable<object> Values;
public int ValueCount = 0;
private static readonly Regex _rx =
new Regex("((?<alpha>[a-z]+)|(?<numeric>([0-9]+)))",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
public ValueChain(string valueString)
{
Values = Parse(valueString);
}
private IEnumerable<object> Parse(string valueString)
{
var matches = _rx.Matches(valueString);
ValueCount = matches.Count;
foreach (var match in matches.Cast<Match>())
{
if (match.Groups["alpha"].Success)
yield return match.Groups["alpha"].Value;
else if (match.Groups["numeric"].Success)
yield return int.Parse(match.Groups["numeric"].Value);
}
}
}
Now this comparer:
public class ValueChainComparer : IComparer<ValueChain>
{
private IComparer<string> StringComparer;
public ValueChainComparer()
: this(global::System.StringComparer.OrdinalIgnoreCase)
{
}
public ValueChainComparer(IComparer<string> stringComparer)
{
StringComparer = stringComparer;
}
#region IComparer<ValueChain> Members
public int Compare(ValueChain x, ValueChain y)
{
//todo: null checks
int comparison = 0;
foreach (var pair in x.Values.Zip
(y.Values, (xVal, yVal) => new { XVal = xVal, YVal = yVal }))
{
//types match?
if (pair.XVal.GetType().Equals(pair.YVal.GetType()))
{
if (pair.XVal is string)
comparison = StringComparer.Compare(
(string)pair.XVal, (string)pair.YVal);
else if (pair.XVal is int) //unboxing here - could be changed
comparison = Comparer<int>.Default.Compare(
(int)pair.XVal, (int)pair.YVal);
if (comparison != 0)
return comparison;
}
else //according to your rules strings are always greater than numbers.
{
if (pair.XVal is string)
return 1;
else
return -1;
}
}
if (comparison == 0) //ah yes, but were they the same length?
{
//whichever one has the most values is greater
return x.ValueCount == y.ValueCount ?
0 : x.ValueCount < y.ValueCount ? -1 : 1;
}
return comparison;
}
#endregion
}
Now you can get the max using OrderByDescending on an IEnumerable<ValueChain> and FirstOrDefault:
[TestMethod]
public void TestMethod1()
{
List<ValueChain> values = new List<ValueChain>(new []
{
new ValueChain("d10a9"),
new ValueChain("d10a10")
});
ValueChain max =
values.OrderByDescending(v => v, new ValueChainComparer()).FirstOrDefault();
}
So you can use this to sort the string values in your dictionary:
var maxKvp = lsd.OrderByDescending(kvp => new ValueChain(kvp.Value),
new ValueChainComparer()).FirstOrDefault();
I have this function -
public int GetAvgResult()
{
var weeklyvalues=GetWeeklyValues();//gets list of weekly values.
if (weeklyvalues.Count == 0)
return 0;
return (weeklyvalues.Sum() / weeklyvalues.Count);
}
Is there a shorter way to write this using ?: or maybe something else ?
public double GetAvgResult()
{
// Assumes GetWeeklyValues() never returns null.
return GetWeeklyValues().DefaultIfEmpty().Average();
}
Do note that this returns a double , which I assume is what you really want (the average of a bunch of integers is logically not an integer). You can cast it to int if necessary, or if you want to stick with integer math all the way:
var seq = GetWeeklyValues().DefaultIfEmpty();
return seq.Sum() / seq.Count();
public int GetAvgResult()
{
var weeklyvalues = GetWeeklyValues();
return (weeklyvalues.Count != 0) ? (weeklyvalues.Sum() / weeklyvalues.Count) : 0;
}
or:
public int GetAvgResult()
{
return GetWeeklyValues().DefaultIfEmpty().Average();
}
public int GetAvgResult()
{
var weeklyvalues = GetWeeklyValues(); //gets list of weekly values.
return weeklyvalues.Count == 0 ? 0 : weeklyvalues.Sum() / weeklyvalues.Count;
}
That's as short as I'd attempt to make it. Is there a specific reason (other than code golf) you're trying for a low character count?
public int GetAvgResult()
{
var weeklyvalues = GetWeeklyValues();//gets list of weekly values.
return (weeklyvalues.Count == 0) ? 0 : (weeklyvalues.Sum() / weeklyvalues.Count );
}
public int GetAvgResult()
{
var weeklyvalues=GetWeeklyValues();//gets list of weekly values.
return weeklyvalues.Count == 0
? 0
: (weeklyvalues.Sum() / weeklyvalues.Count);
}
I've got a large set of data for which computing the sort key is fairly expensive. What I'd like to do is use the DSU pattern where I take the rows and compute a sort key. An example:
Qty Name Supplier
Row 1: 50 Widgets IBM
Row 2: 48 Thingies Dell
Row 3: 99 Googaws IBM
To sort by Quantity and Supplier I could have the sort keys: 0050 IBM, 0048 Dell, 0099 IBM. The numbers are right-aligned and the text is left-aligned, everything is padded as needed.
If I need to sort by the Quanty in descending order I can just subtract the value from a constant (say, 10000) to build the sort keys: 9950 IBM, 9952 Dell, 9901 IBM.
How do I quickly/cheaply build a descending key for the alphabetic fields in C#?
[My data is all 8-bit ASCII w/ISO 8859 extension characters.]
Note: In Perl, this could be done by bit-complementing the strings:
$subkey = $string ^ ( "\xFF" x length $string );
Porting this solution straight into C# doesn't work:
subkey = encoding.GetString(encoding.GetBytes(stringval).
Select(x => (byte)(x ^ 0xff)).ToArray());
I suspect because of the differences in the way that strings are handled in C#/Perl. Maybe Perl is sorting in ASCII order and C# is trying to be smart?
Here's a sample piece of code that tries to accomplish this:
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
List<List<string>> sample = new List<List<string>>() {
new List<string>() { "", "apple", "table" },
new List<string>() { "", "apple", "chair" },
new List<string>() { "", "apple", "davenport" },
new List<string>() { "", "orange", "sofa" },
new List<string>() { "", "peach", "bed" },
};
foreach(List<string> line in sample)
{
StringBuilder sb = new StringBuilder();
string key1 = line[1].PadRight(10, ' ');
string key2 = line[2].PadRight(10, ' ');
// Comment the next line to sort desc, desc
key2 = encoding.GetString(encoding.GetBytes(key2).
Select(x => (byte)(x ^ 0xff)).ToArray());
sb.Append(key2);
sb.Append(key1);
line[0] = sb.ToString();
}
List<List<string>> output = sample.OrderBy(p => p[0]).ToList();
return;
You can get to where you want, although I'll admit I don't know whether there's a better overall way.
The problem you have with the straight translation of the Perl method is that .NET simply will not allow you to be so laissez-faire with encoding. However, if as you say your data is all printable ASCII (ie consists of characters with Unicode codepoints in the range 32..127) - note that there is no such thing as '8-bit ASCII' - then you can do this:
key2 = encoding.GetString(encoding.GetBytes(key2).
Select(x => (byte)(32+95-(x-32))).ToArray());
In this expression I have been explicit about what I'm doing:
Take x (which I assume to be in 32..127)
Map the range to 0..95 to make it zero-based
Reverse by subtracting from 95
Add 32 to map back to the printable range
It's not very nice but it does work.
Just write an IComparer that would work as a chain of comparators.
In case of equality on each stage, it should pass eveluation to the next key part. If it's less then, or greater then, just return.
You need something like this:
int comparision = 0;
foreach(i = 0; i < n; i++)
{
comparision = a[i].CompareTo(b[i]) * comparisionSign[i];
if( comparision != 0 )
return comparision;
}
return comparision;
Or even simpler, you can go with:
list.OrderBy(i=>i.ID).ThenBy(i=>i.Name).ThenByDescending(i=>i.Supplier);
The first call return IOrderedEnumerable<>, the which can sort by additional fields.
Answering my own question (but not satisfactorily). To construct a descending alphabetic key I used this code and then appended this subkey to the search key for the object:
if ( reverse )
subkey = encoding.GetString(encoding.GetBytes(subkey)
.Select(x => (byte)(0x80 - x)).ToArray());
rowobj.sortKey.Append(subkey);
Once I had the keys built, I couldn't just do this:
rowobjList.Sort();
Because the default comparator isn't in ASCII order (which my 0x80 - x trick relies on). So then I had to write an IComparable<RowObject> that used the Ordinal sorting:
public int CompareTo(RowObject other)
{
return String.Compare(this.sortKey, other.sortKey,
StringComparison.Ordinal);
}
This seems to work. I'm a little dissatisfied because it feels clunky in C# with the encoding/decoding of the string.
If a key computation is expensive, why compute a key at all? String comparision by itself is not free, it's actually expensive loop through the characters and is not going to perform any better then a custom comparision loop.
In this test custom comparision sort performs about 3 times better then DSU.
Note that DSU key computation is not measured in this test, it's precomputed.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace DSUPatternTest
{
[TestClass]
public class DSUPatternPerformanceTest
{
public class Row
{
public int Qty;
public string Name;
public string Supplier;
public string PrecomputedKey;
public void ComputeKey()
{
// Do not need StringBuilder here, String.Concat does better job internally.
PrecomputedKey =
Qty.ToString().PadLeft(4, '0') + " "
+ Name.PadRight(12, ' ') + " "
+ Supplier.PadRight(12, ' ');
}
public bool Equals(Row other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.Qty == Qty && Equals(other.Name, Name) && Equals(other.Supplier, Supplier);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (Row)) return false;
return Equals((Row) obj);
}
public override int GetHashCode()
{
unchecked
{
int result = Qty;
result = (result*397) ^ (Name != null ? Name.GetHashCode() : 0);
result = (result*397) ^ (Supplier != null ? Supplier.GetHashCode() : 0);
return result;
}
}
}
public class RowComparer : IComparer<Row>
{
public int Compare(Row x, Row y)
{
int comparision;
comparision = x.Qty.CompareTo(y.Qty);
if (comparision != 0) return comparision;
comparision = x.Name.CompareTo(y.Name);
if (comparision != 0) return comparision;
comparision = x.Supplier.CompareTo(y.Supplier);
return comparision;
}
}
[TestMethod]
public void CustomLoopIsFaster()
{
var random = new Random();
var rows = Enumerable.Range(0, 5000).Select(i =>
new Row
{
Qty = (int) (random.NextDouble()*9999),
Name = random.Next().ToString(),
Supplier = random.Next().ToString()
}).ToList();
foreach (var row in rows)
{
row.ComputeKey();
}
var dsuSw = Stopwatch.StartNew();
var sortedByDSU = rows.OrderBy(i => i.PrecomputedKey).ToList();
var dsuTime = dsuSw.ElapsedMilliseconds;
var customSw = Stopwatch.StartNew();
var sortedByCustom = rows.OrderBy(i => i, new RowComparer()).ToList();
var customTime = customSw.ElapsedMilliseconds;
Trace.WriteLine(dsuTime);
Trace.WriteLine(customTime);
CollectionAssert.AreEqual(sortedByDSU, sortedByCustom);
Assert.IsTrue(dsuTime > customTime * 2.5);
}
}
}
If you need to build a sorter dynamically you can use something like this:
var comparerChain = new ComparerChain<Row>()
.By(r => r.Qty, false)
.By(r => r.Name, false)
.By(r => r.Supplier, false);
var sortedByCustom = rows.OrderBy(i => i, comparerChain).ToList();
Here is a sample implementation of comparer chain builder:
public class ComparerChain<T> : IComparer<T>
{
private List<PropComparer<T>> Comparers = new List<PropComparer<T>>();
public int Compare(T x, T y)
{
foreach (var comparer in Comparers)
{
var result = comparer._f(x, y);
if (result != 0)
return result;
}
return 0;
}
public ComparerChain<T> By<Tp>(Func<T,Tp> property, bool descending) where Tp:IComparable<Tp>
{
Comparers.Add(PropComparer<T>.By(property, descending));
return this;
}
}
public class PropComparer<T>
{
public Func<T, T, int> _f;
public static PropComparer<T> By<Tp>(Func<T,Tp> property, bool descending) where Tp:IComparable<Tp>
{
Func<T, T, int> ascendingCompare = (a, b) => property(a).CompareTo(property(b));
Func<T, T, int> descendingCompare = (a, b) => property(b).CompareTo(property(a));
return new PropComparer<T>(descending ? descendingCompare : ascendingCompare);
}
public PropComparer(Func<T, T, int> f)
{
_f = f;
}
}
It works a little bit slower, maybe because of property binging delegate calls.