I have to find parser for solving equation with unknows. User enters two numbers and the parser parses equation to solve. For example:
Equation:
x^3 + y^2 + sin60°
User input:
x=4, y=2
It have to be done using AForge.NET framework. Console app/WinForms
I have been looking for it in AForge .NET documentation but can't find something matches my problem.
I don't believe there is an evaluator for regular mathematical expressions in AForge, but you could use the PolishExpression class to evaluate an expression written in reverse polish notation:
using AForge;
string expression = "$0 $0 $0 * * $1 $1 * + " + (Math.PI / 3) + " sin +";
// variables for the expression
double x = 4;
double y = 2;
double[] vars = new double[] { x, y };
// expression evaluation
double result = PolishExpression.Evaluate(expression, vars);
Console.WriteLine("Polish expression evaluation: " + result);
This yields the value 68.86602540378443. There's also no direct exponential operator, so I just multiplied x by itself three times. But you could use logaritms instead:
string expression = "$0 ln 3 * exp $1 ln 2 * exp + " + (Math.PI / 3) + " sin +";
You can also confirm that this is the same as you'd get in C# (note that you must convert 60 degrees to radians in order to use Math.sin):
double TestEvaluate(double x, double y)
{
// x^3 + y^2 + sin60°
// Note that Math.sin accepts radians, so we must convert 60°
return Math.Pow(x, 3) + Math.Pow(y, 2) + Math.Sin((Math.PI / 180) * 60);
}
Console.WriteLine("C# evalution: " + TestEvaluate(x, y));
But yeah, I'd recommend using another library if you don't want to use polish notation in your expressions.
I also tried the code example above with "AForge.Math.Expression", but I don't believe this class exists in AForge.Math 2.2.5.
Solution
It seems you need an expression parser. I decided to write one, that uses System.Linq.Expression for building expression trees. I started with the simple expression parser from https://github.com/toptensoftware/SimpleExpressionEngine and build from there.
Example Code
First the fun stuff. Look at this test code
public static void TestSOExpr()
{
string expression = "x^3 + y^2 + sin(60°)";
Parser parser = new Parser(expression);
Console.WriteLine("Expression: ");
Console.WriteLine(parser.Expression);
Console.WriteLine("Arguments: ");
foreach (var item in parser.Arguments)
{
Console.WriteLine(item);
}
Func<double,double,double> f = parser.CompileBinary();
double x = 4, y = 2;
Console.WriteLine($"f({x},{y}) = {f(x, y)}");
}
with output
Expression:
(((x ^ 3) + (y * y)) + sin((60 * 0.0174532925199433)))
Arguments:
x
y
f(4,2) = 68.8660254037844
The result is numerically correct. A lot of time was spent in trying to deal with ° as a constant value with implied multiplication. I am happy with the results.
Class Structure
To get there there are three classes needed
Token
This information class holds the data extracted from the string such as names, values, operators, functions and parenthesis.
public enum TokenType
{
EOF,
Operator,
StartBlock,
EndBlock,
Delimiter,
Identifier,
Number,
}
public readonly struct Token
{
public static readonly Token Empty = new Token(TokenType.EOF, string.Empty, 0, 0);
public Token(Token token, int level) : this()
{
this = token;
Level = level;
}
Token(TokenType type, string symbol, double number, int level) : this()
{
Type = type;
Symbol = symbol;
Number = number;
Level = level;
}
public static Token Operator(char symbol, int level = 0) => new Token(TokenType.Operator, symbol.ToString(), 0, level);
public static Token StartBlock(char symbol, int level = 0) => new Token(TokenType.StartBlock, symbol.ToString(), 0, level);
public static Token EndBlock(char symbol, int level = 0) => new Token(TokenType.EndBlock, symbol.ToString(), 0, level);
public static Token Delimiter(char symbol, int level = 0) => new Token(TokenType.Delimiter, symbol.ToString(), 0, level);
public static Token Identifier(string symbol, int level = 0) => new Token(TokenType.Identifier, symbol, 0, level);
public static Token Value(double number, int level = 0) => new Token(TokenType.Number, string.Empty, number, level);
public static Token operator +(Token token, int delta) => new Token(token, token.Level + delta);
public static Token operator -(Token token, int delta) => new Token(token, token.Level - delta);
public TokenType Type { get; }
public string Symbol { get; }
public double Number { get; }
public int Level { get; }
public bool IsEOF() => Type == TokenType.EOF;
public bool IsOperator(char #operator) => IsOperator(out char op) && op == #operator;
public bool IsOperator(out char #operator)
{
if (Type == TokenType.Operator)
{
#operator = Symbol[0];
return true;
}
#operator = '\0';
return false;
}
public bool IsIdentifier(string symbol) => IsIdentifier(out string x) && x.Equals(symbol);
public bool IsIdentifier(out string symbol)
{
if (Type == TokenType.Identifier)
{
symbol = Symbol;
return true;
}
symbol = string.Empty;
return false;
}
public bool IsNumber(double value) => IsNumber(out double x) && x.Equals(value);
public bool IsNumber(out double value)
{
if (Type == TokenType.Number)
{
value = Number;
return true;
}
value = 0;
return false;
}
public bool IsStartBlock(char delimiter) => IsStartBlock(out char sym) && sym == delimiter;
public bool IsStartBlock(out char delimiter)
{
if (Type == TokenType.StartBlock)
{
delimiter = Symbol[0];
return true;
}
delimiter = '\0';
return false;
}
public bool IsEndBlock(char delimiter) => IsEndBlock(out char sym) && sym == delimiter;
public bool IsEndBlock(out char delimiter)
{
if (Type == TokenType.EndBlock)
{
delimiter = Symbol[0];
return true;
}
delimiter = '\0';
return false;
}
public bool IsDelimiter(char delimiter) => IsDelimiter(out char sym) && sym == delimiter;
public bool IsDelimiter(out char delimiter)
{
if (Type == TokenType.Delimiter)
{
delimiter = Symbol[0];
return true;
}
delimiter = '\0';
return false;
}
public override string ToString()
{
var tab = new string('\t', Level);
switch (Type)
{
case TokenType.EOF:
return $"{tab}{Type}";
case TokenType.Operator:
case TokenType.StartBlock:
case TokenType.EndBlock:
case TokenType.Delimiter:
case TokenType.Identifier:
return $"{tab}{Type}: {Symbol}";
case TokenType.Number:
return $"{tab}{Type}: {Number}";
default:
throw new NotSupportedException();
}
}
}
Tokenizer
The job of the tokenizer is to extract the Token from a string. This is done one step at a time with the .MoveNext() and .Reset() functions. At each step the .Current property holds the token information.
/// <summary>
/// Parses strings into Tokens for further processing.
/// </summary>
/// <remarks>Code taken from https://github.com/toptensoftware/SimpleExpressionEngine </remarks>
public class Tokenizer : IEnumerator<Token>, IEnumerable<Token>
{
readonly string _expression;
TextReader _reader;
char _currentChar;
public Tokenizer(string expression)
{
_expression = expression;
Reset();
}
public Token Current { get; private set; }
object System.Collections.IEnumerator.Current { get => Current; }
public IEnumerator<Token> GetEnumerator() => this;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
public void Reset()
{
_reader = new StringReader(_expression);
NextChar();
//MoveNext();
}
// Read the next character from the input strem
// and store it in _currentChar, or load '\0' if EOF
void NextChar()
{
int ch = _reader.Read();
_currentChar = ch < 0 ? '\0' : (char)ch;
}
public bool MoveNext()
{
// Skip whitespace
while (char.IsWhiteSpace(_currentChar))
{
NextChar();
}
switch (_currentChar)
{
case '\0':
{
if (Current.Level > 0)
{
throw new InvalidOperationException();
}
Current = Token.Empty;
return false;
}
case '+':
case '-':
case '*':
case '/':
case '^':
case '=':
{
Current = Token.Operator(_currentChar, Current.Level);
NextChar();
return true;
}
case '(':
{
Current = Token.StartBlock(_currentChar, Current.Level + 1);
NextChar();
return true;
}
case ')':
{
Current = Token.EndBlock(_currentChar, Current.Level - 1);
NextChar();
return true;
}
case ';':
case ',':
{
Current = Token.Delimiter(_currentChar, Current.Level);
NextChar();
return true;
}
}
if (char.IsDigit(_currentChar) || _currentChar == '.')
{
// Capture digits/decimal point
StringBuilder sb = new StringBuilder();
bool haveDecimalPoint = false;
while (char.IsDigit(_currentChar) || (!haveDecimalPoint && _currentChar == '.'))
{
sb.Append(_currentChar);
haveDecimalPoint = _currentChar == '.';
NextChar();
}
// Parse it
if (double.TryParse(sb.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out double x))
{
Current = Token.Value(x, Current.Level);
//Token = Token.Number;
return true;
}
}
// Identifier - starts with letter or underscore
if (char.IsLetter(_currentChar) || _currentChar == '_' || _currentChar == '°')
{
var sb = new StringBuilder();
// Accept letter, digit or underscore
while (char.IsLetterOrDigit(_currentChar) || _currentChar == '_' || _currentChar == '°')
{
sb.Append(_currentChar);
NextChar();
}
// Setup token
Current = Token.Identifier(sb.ToString(), Current.Level);
return true;
}
Current = Token.Empty;
return false;
}
void IDisposable.Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
void Dispose(bool disposed)
{
if (disposed)
{
_reader.Dispose();
}
}
}
Parser
This is the main class that recursively builds the expression tree. When the constructor is called, the resulting expression tree is stored in the .Expression property, as well as any required arguments in .Arguments.
/// <summary>
/// Parses a string into an <see cref="Expression"/> tree.
/// </summary>
public class Parser
{
readonly Tokenizer _tokenizer;
readonly Dictionary<string, ParameterExpression> _arguments;
public Parser(string expression) : this(new Tokenizer(expression)) { }
public Parser(Tokenizer tokenizer)
{
this._tokenizer = tokenizer;
this._arguments = new Dictionary<string, ParameterExpression>();
Expression = ParseTokens();
}
public Expression Expression { get; }
public IReadOnlyList<ParameterExpression> Arguments => _arguments.Values.ToList();
public Func<double> CompileNonary()
{
if (Arguments.Count == 0)
{
return Compile<Func<double>>();
}
throw new InvalidOperationException("Expression has too many arguments.");
}
public Func<double, double> CompileUnary()
{
if (Arguments.Count == 1)
{
return Compile<Func<double, double>>();
}
if (Arguments.Count > 1)
{
throw new InvalidOperationException("Expression has too many arguments.");
}
else
{
throw new InvalidOperationException("Expression has too few arguments.");
}
}
public Func<double, double, double> CompileBinary()
{
if (Arguments.Count == 2)
{
return Compile<Func<double, double, double>>();
}
if (Arguments.Count > 2)
{
throw new InvalidOperationException("Expression has too many arguments.");
}
else
{
throw new InvalidOperationException("Expression has too few arguments.");
}
}
TFunc Compile<TFunc>() where TFunc : Delegate
{
ParameterExpression[] arguments = _arguments.Values.ToArray();
return Expression.Lambda<TFunc>(Expression, arguments).Compile() as TFunc;
}
Expression ParseTokens()
{
_tokenizer.Reset();
if (_tokenizer.MoveNext())
{
Expression expr = ParseEquals();
// Check everything was consumed
if (_tokenizer.Current.Type != TokenType.EOF)
{
throw new InvalidOperationException("Unexpected characters at end of expression");
}
return expr;
}
throw new InvalidOperationException("Invalid Expression");
}
Expression ParseEquals()
{
Expression lhs = ParseAddSubtract();
while (true)
{
if (_tokenizer.Current.IsOperator('='))
{
_tokenizer.MoveNext();
Expression rhs = ParseAddSubtract();
lhs = Expression.Equal(lhs, rhs);
}
else
{
return lhs;
}
}
}
Expression ParseAddSubtract()
{
Expression lhs = ParseMulDivide();
while (true)
{
if (_tokenizer.Current.IsOperator('+'))
{
_tokenizer.MoveNext();
Expression rhs = ParseMulDivide();
lhs = Expression.Add(lhs, rhs);
}
else if (_tokenizer.Current.IsOperator('-'))
{
_tokenizer.MoveNext();
Expression rhs = ParseMulDivide();
lhs = Expression.Subtract(lhs, rhs);
}
else
{
return lhs;
}
}
}
Expression ParseMulDivide()
{
Expression lhs = ParsePower();
bool negate = false;
if (lhs is ConstantExpression lex)
{
double x = Convert.ToDouble(lex.Value);
if (x == 0)
{
return Expression.Constant(0.0);
}
else if (x == -1)
{
negate = true;
}
if (_tokenizer.Current.IsIdentifier(out string name))
{
// 60π - or example
Expression rhs = ParseLeaf();
return Expression.Multiply(lhs, rhs);
}
}
while (true)
{
if (_tokenizer.Current.IsOperator('*'))
{
_tokenizer.MoveNext();
Expression rhs = ParsePower();
if (rhs is ConstantExpression rex)
{
double y = Convert.ToDouble(rex.Value);
if (y == 0)
{
lhs = Expression.Constant(0.0);
}
else if (y == 1)
{
// do nothing
}
else if (y == -1)
{
negate = !negate;
}
else
{
lhs = Expression.Multiply(lhs, rhs);
}
}
else
{
lhs = Expression.Multiply(lhs, rhs);
}
}
else if (_tokenizer.Current.IsOperator('/'))
{
_tokenizer.MoveNext();
Expression rhs = ParsePower();
if (rhs is ConstantExpression rex)
{
double y = Convert.ToDouble(rex.Value);
if (y == 0)
{
lhs = Expression.Constant(double.PositiveInfinity);
}
else if (y == 1)
{
// do nothing
}
else if (y == -1)
{
negate = !negate;
}
else
{
lhs = Expression.Divide(lhs, rhs);
}
}
else
{
lhs = Expression.Divide(lhs, rhs);
}
}
else
{
return negate ? Expression.Negate(lhs) : lhs;
}
}
}
Expression ParsePower()
{
Expression lhs = ParseUnary();
while (true)
{
if (_tokenizer.Current.IsOperator('^'))
{
_tokenizer.MoveNext();
Expression rhs = ParseUnary();
if (rhs is ConstantExpression cex)
{
double x = Convert.ToDouble(cex.Value);
if (x == 0)
{
return Expression.Constant(1.0);
}
if (x == 1)
{
return lhs;
}
if (x == -1)
{
return Expression.Divide(Expression.Constant(1.0), lhs);
}
if (x == 2)
{
return Expression.Multiply(lhs, lhs);
}
}
lhs = Expression.Power(lhs, rhs);
}
else
{
return lhs;
}
}
}
Expression ParseUnary()
{
while (true)
{
if (_tokenizer.Current.IsOperator('+'))
{
// ignore unary +
_tokenizer.MoveNext();
continue;
}
if (_tokenizer.Current.IsOperator('-'))
{
_tokenizer.MoveNext();
Expression rhs = ParseUnary();
if (rhs is UnaryExpression uex)
{
if (uex.NodeType == ExpressionType.Negate)
{
return uex.Operand;
}
}
return Expression.Negate(rhs);
}
return ParseLeaf();
}
}
private Expression ParseLeaf()
{
if (_tokenizer.Current.IsNumber(out double x))
{
_tokenizer.MoveNext();
return Expression.Constant(x);
}
if (_tokenizer.Current.IsStartBlock('('))
{
_tokenizer.MoveNext();
Expression node = ParseAddSubtract();
if (!_tokenizer.Current.IsEndBlock(')'))
{
throw new InvalidOperationException("Mismatched Parenthesis.");
}
_tokenizer.MoveNext();
return node;
}
if (_tokenizer.Current.IsIdentifier(out string name))
{
_tokenizer.MoveNext();
if (_tokenizer.Current.IsStartBlock('('))
{
// function call
_tokenizer.MoveNext();
// Parse arguments
List<Expression> arguments = new List<Expression>();
while (true)
{
Expression node = ParseAddSubtract();
arguments.Add(node);
if (_tokenizer.Current.IsDelimiter(','))
{
_tokenizer.MoveNext();
continue;
}
// end of arguments
break;
}
if (!_tokenizer.Current.IsEndBlock(')'))
{
throw new InvalidOperationException("Mismatched Parenthesis.");
}
_tokenizer.MoveNext();
MethodInfo f = typeof(MathFunctions).GetMethod(name, BindingFlags.Static | BindingFlags.Public);
if (f != null)
{
switch (arguments.Count)
{
case 0:
return Expression.Call(f);
case 1:
return Expression.Call(f, arguments[0]);
case 2:
return Expression.Call(f, arguments[0], arguments[1]);
default:
throw new InvalidOperationException($"Too many arguments for function {name}.");
}
}
else
{
throw new InvalidOperationException($"Unknown function {name}.");
}
}
else
{
if (MathFunctions.knownConstants.ContainsKey(name))
{
// named constant
return Expression.Constant(MathFunctions.knownConstants[name]);
}
// variable
if (!_arguments.ContainsKey(name))
{
// add to list of arguments
_arguments.Add(name, Expression.Parameter(typeof(double), name));
}
return _arguments[name];
}
}
if (!_tokenizer.Current.IsEOF())
{
throw new InvalidOperationException($"Unexpected token {_tokenizer.Current}");
}
throw new NotImplementedException();
}
}
MathFunctions
The parser depends on a utility function to provide method bodies for math functions. Most are wrappers around the Math class, but some are new. The names are lowercase to match with any functions called in the parsed string.
public static class MathFunctions
{
internal static readonly Random rng = new Random();
internal static readonly Dictionary<string, double> knownConstants =
new Dictionary<string, double>()
{
["pi"] = Math.PI,
["π"] = Math.PI,
["e"] = Math.E,
["Φ"] = (1 + Math.Sqrt(5)) / 2,
["°"] = Math.PI / 180,
["deg"] = Math.PI / 180,
["rad"] = 180 / Math.PI,
["rpm"] = 2 * Math.PI / 60,
};
public static double rand() => rng.NextDouble();
public static double abs(double x) => Math.Abs(x);
public static double sqr(double x) => x * x;
public static double sqrt(double x) => Math.Sqrt(x);
public static double sign(double x) => (double)Math.Sign(x);
public static double floor(double x) => Math.Floor(x);
public static double round(double x) => Math.Round(x);
public static double exp(double x) => Math.Exp(x);
public static double log(double x) => Math.Log(x);
public static double sin(double x) => Math.Sin(x);
public static double cos(double x) => Math.Cos(x);
public static double tan(double x) => Math.Tan(x);
public static double asin(double x) => Math.Asin(x);
public static double acos(double x) => Math.Acos(x);
public static double atan(double x) => Math.Atan(x);
public static double sinh(double x) => Math.Sinh(x);
public static double cosh(double x) => Math.Cosh(x);
public static double tanh(double x) => Math.Tanh(x);
public static double asinh(double x) => Math.Log(Math.Sqrt(x * x + 1) + x);
public static double acosh(double x) => Math.Log(Math.Sqrt(x * x - 1) + x);
public static double atanh(double x) => -Math.Log((1 - x) / (1 + x)) / 2;
public static double pow(double x, double y) => Math.Pow(x, y);
public static double atan2(double dy, double dx) => Math.Atan2(dy, dx);
public static double min(double x, double y) => Math.Min(x, y);
public static double max(double x, double y) => Math.Max(x, y);
}
As a final note the parser also supports some named constants
"pi" = Math.PI,
"π" = Math.PI,
"e" = Math.E,
"Φ" = (1 + Math.Sqrt(5)) / 2,
"°" = Math.PI / 180,
"deg" = Math.PI / 180,
"rad" = 180 / Math.PI,
"rpm" = 2 * Math.PI / 60,
Such that inputs such as 6π and 60° and 1000rpm are supported.
Compiling
The .NET Expression class has a .Compile() method that can be used to turn the expression into a function. Wrappers around this method exists in the parser such that CompileUnary() and CompileBinary() produce functions of the form f(x) and f(x,y) respectively with arguments and return types that of double numbers.
From the example code above this is demonstrated with
var f = parser.CompileBinary();
double x = ...
double y = ...
double z = f(x, y);
I would like to refactor my method. I also need to get which value was first? So which anyOf? Is it possible to get it from here?
Example:
List<string> anyOf = new List<string>(){"at", "near", "by", "above"};
string source = "South Branch Raritan River near High Bridge at NJ"
public static int IndexOfAny(this string source, IEnumerable<string> anyOf, StringComparison stringComparisonType = StringComparison.CurrentCultureIgnoreCase)
{
var founds = anyOf
.Select(sub => source.IndexOf(sub, stringComparisonType))
.Where(i => i >= 0);
return founds.Any() ? founds.Min() : -1;
}
I would like to get back what is first in string. "near" or "at".
You could use:
public static (int index, string? firstMatch) IndexOfAny(this string source, IEnumerable<string> anyOf, StringComparison stringComparisonType = StringComparison.CurrentCultureIgnoreCase)
{
return anyOf
.Select(s => (Index: source.IndexOf(s, stringComparisonType), String: s))
.Where(x => x.Index >= 0)
.DefaultIfEmpty((-1, null))
.First();
}
I couldn't resist creating a more efficient implementation.
Working here.
Whilst this looks more complicated, its better because,
It allocates only,
an array for the valid search terms,
a array of indices for each search term and,
an array of lengths for each search term.
The source text is enumerated only once and, if a match is found,
that loop will exit early.
Additionally, the code incorporates parameter checking which you'll want as extension methods should be resusable.
public static class Extensions
{
public static int IndexOfAny<T>(
this IEnumerable<T> source,
IEnumerable<IEnumerable<T>> targets,
IEqualityComparer<T> comparer = null)
{
// Parameter Handling
comparer = comparer ?? EqualityComparer<T>.Default;
ArgumentNullException.ThrowIfNull(targets);
var clean = targets
.Where(t => t != null)
.Select(t => t.ToArray())
.Where(t => t.Length > 0)
.ToArray();
if (clean.Length == 0)
{
throw new ArgumentException(
$"'{nameof(targets)}' does not contain a valid search sequence");
}
// Prep
var lengths = clean.Select(t => t.Length).ToArray();
var indices = clean.Select(_ => 0).ToArray();
int i = 0;
// Process
foreach(var t in source)
{
i++;
for(var j = 0; j < clean.Length; j++)
{
var index = indices[j];
if (comparer.Equals(clean[j][index], t))
{
index += 1;
if (index == lengths[j])
{
return i - lengths[j];
}
indices[j] = index;
}
else
{
if (index != 0)
{
indices[j] = 0;
}
}
}
}
return -1;
}
public static int IndexOfAny(
this string source,
IEnumerable<string> targets,
StringComparer comparer = null)
{
comparer = comparer ?? StringComparer.Ordinal;
ArgumentNullException.ThrowIfNull(targets);
return source.ToCharArray().IndexOfAny(
targets.Select(t => t.ToCharArray()),
new CharComparerAdapter(comparer));
}
}
public class CharComparerAdapter : IEqualityComparer<char>
{
private StringComparer Comparer { get; }
public CharComparerAdapter(StringComparer comparer)
{
ArgumentNullException.ThrowIfNull(comparer);
Comparer = comparer;
}
public bool Equals(char left, char right)
{
return Comparer.Equals(left.ToString(), right.ToString());
}
public int GetHashCode(char v)
{
return v;
}
}
my TIME Enum contains Annual, Monthly, weekly, daily and Hourly.
Here I want to decide which is the minimum and want to return that.
How can I do this ? Here is the code I tried.
private Time DecideMinTime(IEnumerable<Time> g)
{
var minTime = Time.Hourly;
foreach (var element in g)
{
minTime = element;
}
return minTime;
}
Assuming that the numeric value of the enum elements decides what the minimum is:
private Time DecideMinTime(IEnumerable<Time> g)
{
if (g == null) { throw new ArgumentNullException("g"); }
return (Time)g.Cast<int>().Min();
}
If the numeric values indicate the opposite order then you would use .Max() instead of .Min().
As indicated, the numeric order is not consistent. This can be worked around simply by using a mapping indicating the correct order:
static class TimeOrdering
{
private static readonly Dictionary<Time, int> timeOrderingMap;
static TimeOrdering()
{
timeOrderingMap = new Dictionary<Time, int>();
timeOrderingMap[Time.Hourly] = 1;
timeOrderingMap[Time.Daily] = 2;
timeOrderingMap[Time.Weekly] = 3;
timeOrderingMap[Time.Monthly] = 4;
timeOrderingMap[Time.Annual] = 5;
}
public Time DecideMinTime(IEnumerable<Time> g)
{
if (g == null) { throw new ArgumentNullException("g"); }
return g.MinBy(i => timeOrderingMap[i]);
}
public TSource MinBy<TSource, int>(
this IEnumerable<TSource> self,
Func<TSource, int> ordering)
{
if (self == null) { throw new ArgumentNullException("self"); }
if (ordering == null) { throw new ArgumentNullException("ordering"); }
using (var e = self.GetEnumerator()) {
if (!e.MoveNext()) {
throw new ArgumentException("Sequence is empty.", "self");
}
var minElement = e.Current;
var minOrder = ordering(minElement);
while (e.MoveNext()) {
var curOrder = ordering(e.Current);
if (curOrder < minOrder) {
minOrder = curOrder;
minElement = e.Current;
}
}
return minElement;
}
}
}
To make it easier you can assign int values to your enum:
enum Time : byte {Hourly=1, Daily=2, Weekly=3, Monthly=4, Annual=5};
and then
private static Time DecideMinTime(IEnumerable<Time> g)
{
return g.Min();
}
That way you avoid casting back and forth.
I got a lot of data from a database, which are results from a search function. Now I've a List<string[]> which has duplicated elements of type string[]. The string[] in the list are the search results.
I know that every new created array has a different instance so i can't use MyListOfArrays.Distinct().ToList().
Maybe it's a very basic question...
My question is, are there any functions built in to remove a duplicated string[] form the List<string[]>? Or do I have to write it by my selfe?
Thank you
You can use distinct method with custom equalityComparer
IEnumerable<string[]> distinct = inputStringArrayList.Distinct(new EqualityComparer());
EqualityComparer
class EqualityComparer : IEqualityComparer<string[]>
{
public bool Equals(string[] x, string[] y)
{
if (x.Length != y.Length)
{
return false;
}
if (x.Where((t, i) => t != y[i]).Any())
{
return false;
}
return true;
}
public int GetHashCode(string[] obj)
{
return obj.GetHashCode();
}
}
Alternative Equals Method
public bool Equals(string[] x, string[] y)
{
return x.SequenceEqual(y);
}
Here I am assuming you are having exact same string arrays with same content at same index.
Correction from Matthew Watson
public int GetHashCode(string[] obj)
{
if (obj == null)
return 0;
int hash = 17;
unchecked
{
foreach (string s in obj)
hash = hash*23 + ((s == null) ? 0 : s.GetHashCode());
}
return hash;
}
I have corrected the answer from #Muctadir Dinar.
(He deserves credit for the answer - I am just correcting it and providing a complete test program):
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
sealed class EqualityComparer: IEqualityComparer<string[]>
{
public bool Equals(string[] x, string[] y)
{
if (ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
return x.SequenceEqual(y);
}
public int GetHashCode(string[] obj)
{
if (obj == null)
return 0;
int hash = 17;
unchecked
{
foreach (string s in obj)
hash = hash*23 + ((s == null) ? 0 : s.GetHashCode());
}
return hash;
}
}
class Program
{
private void run()
{
var list = new List<string[]>
{
strings(1, 10),
strings(2, 10),
strings(3, 10),
strings(2, 10),
strings(4, 10)
};
dump(list);
Console.WriteLine();
var result = list.Distinct(new EqualityComparer());
dump(result);
}
static void dump(IEnumerable<string[]> list)
{
foreach (var array in list)
Console.WriteLine(string.Join(",", array));
}
static string[] strings(int start, int count)
{
return Enumerable.Range(start, count)
.Select(element => element.ToString())
.ToArray();
}
static void Main(string[] args)
{
new Program().run();
}
}
}
A simple and not very efficient approach would be to use string.Join on the string[]:
list = list
.GroupBy(strArr => string.Join("|", strArr))
.Select(g => g.First())
.ToList();
I'm new to LINQ. I need to compute new_id as follows:
public class C_Movement
{
public int id=-1;
public static ObservableCollection<C_Movement> list=new ObservableCollection<C_Movement>();
// ...
}
int new_id = (C_Movement.list.Count==0) ? 0 : C_Movement.list.Max(x => x.id)+1;
Is there a LINQ way to compact that expression, so that I don't have to use the ? : structure? The problem is that, when C_Movement.list contains no elements, C_Movement.list.Max(x => x.id) returns null (and I would like it to return -1, instead).
Thank you.
DefaultIfEmpty method should help:
int new_id = C_Movement.list.Select(x => x.id).DefaultIfEmpty(-1).Max()+1;
int new_id = C_Movement.list.Max(x => (int?)x.id).GetValueOrDefault(-1) + 1;
where GetValueOrDefault is a method of Nullable<T>.
How about:
int new_id = 0;
if (C_Movement.list.Any())
new_id = C_Movement.list.Max(x => x.id) + 1;
Try this
public static class LinqExtensions
{
public static TValue Max<TSource, TValue>(this IEnumerable<TSource> source, Func<TSource, TValue> selector, TValue defaultValueIfEmpty)
where TValue : IComparable
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (selector == null)
throw new ArgumentNullException(nameof(selector));
TValue sum;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (!enumerator.MoveNext())
return defaultValueIfEmpty;
sum = selector(enumerator.Current);
while (enumerator.MoveNext())
{
var num2 = selector(enumerator.Current);
if (num2.CompareTo(sum) > 0)
sum = num2;
}
}
return sum;
}
public static TSource Max<TSource>(this IEnumerable<TSource> source, TSource defaultValueIfEmpty)
where TSource : IComparable
{
if (source == null)
throw new ArgumentNullException(nameof(source));
TSource sum;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (!enumerator.MoveNext())
return defaultValueIfEmpty;
sum = enumerator.Current;
while (enumerator.MoveNext())
{
var num2 = enumerator.Current;
if (num2.CompareTo(sum) > 0)
sum = num2;
}
}
return sum;
}
}