c# - una - macro filtro avanzado varios criterios
¿Cuál es una manera elegante de verificar si 3 variables son iguales cuando cualquiera de ellas puede ser un comodín? (9)
Digamos que tengo 3 variables char
, a
, b
c
.
Cada uno puede ser ''0''
, que es un caso especial y significa que coincide con cada carácter.
Entonces, si a es ''0''
, solo necesito verificar si b == c
.
Quiero verificar si a == b == c
, pero encontré que la implementación en C # es caótica y larga.
¿Hay alguna solución creativa o bonita que puedas ofrecer?
actualizar
Para el rendimiento impulsado, tomar el enfoque de Erik A. Brandstadmoen. Para simplificar, use el apprach de M4N, también hice algunas modificaciones !(query.Any() && query.Distinct().Skip(1).Any())
La solución elegante
Esto requiere un conocimiento básico de LINQ y se basa en la solución de M4N :
static bool IsMatch(params char[] chars)
{
return chars.Where(c => c != ''0'')
.Distinct().Count() <= 1;
}
Editar
Originalmente, mi solución era diferente de la solución de M4N, pero después de algunas simplificaciones, llegué a algo (casi) exactamente igual. Mientras que los créditos van completamente a él, lo dejo como referencia.
Devuelve true
cuando hay como máximo un carácter distinto no comodín en la colección.
Hice que acepte un número variable de parámetros, así que puedes llamarlo para 2, 3 o más caracteres:
bool match = IsMatch(''3'', ''3'', ''4'', ''0'');
La solución simple
Esto es pura traducción de su lógica en su código, no hay cosas extravagantes.
static bool IsMatch(char x, char y)
{
return x == y || x == ''0'' || y == ''0'';
}
static bool IsMatch(char a, char b, char c)
{
return IsMatch(a, b) && IsMatch(b, c) && IsMatch(a, c);
}
La primera sobrecarga de IsMatch
devuelve true
cuando su argumento es igual o uno de ellos es ''0''
.
La segunda sobrecarga simplemente llama a la primera para cada par.
(Tenga en cuenta que debido a los comodines no podemos usar la propiedad transitiva y comparar solo dos pares).
¿Cuenta esto como caótico y largo?
Me parece bien, siempre que solo puedas tener a los tres ...
return ((a == "0" || b == "0" || a == b) && (b =="0" || c =="0" || b == c) && (a =="0" || c =="0" || a == c));
Algo como esto debería funcionar para cualquier número de valores char:
public class Comparer
{
public static bool AreEqualOrZero(params char[] values)
{
var firstNonZero = values.FirstOrDefault(x => x != ''0'');
return values.All(x => x == firstNonZero || x == ''0'');
}
}
Pasa las siguientes pruebas unitarias:
[TestClass()]
public class ComparerTest
{
[TestMethod()]
public void Matches_With_Wildcard()
{
char[] values = {''0'', ''1'', ''1'', ''1''};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Matches_With_No_Wildcard()
{
char[] values = {''1'', ''1'', ''1'', ''1''};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Matches_With_Only_Wildcards()
{
char[] values = {''0'', ''0'', ''0''};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Matches_With_Zero_Length()
{
char[] values = {};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Matches_With_One_Element()
{
char[] values = {''9''};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Matches_With_One_Wildcard_And_Nothing_Else()
{
char[] values = {''0''};
Assert.IsTrue(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Does_Not_Match_On_NonEqual_Sequence_No_Wildcard()
{
char[] values = {''1'', ''2'', ''1'', ''1''};
Assert.IsFalse(Comparer.AreEqualOrZero(values));
}
[TestMethod()]
public void Does_Not_Match_On_NonEqual_Sequence_With_Wildcard()
{
char[] values = {''1'', ''2'', ''1'', ''0''};
Assert.IsFalse(Comparer.AreEqualOrZero(values));
}
}
Algo como esto:
var a = ''1'';
var b = ''0'';
var c = ''1'';
var chars = new List<char> { a, b, c };
var filtered = chars.Where(ch => ch != ''0'');
var allEqual = filtered.Count() == 0 || filtered.Distinct().Count() == 1;
Para explicar la solución:
- Primero pon todos los caracteres en una lista
- excluye todos los caracteres que son ''0'':
.Where(ch => ch != ''0'')
- todos los caracteres restantes son iguales si:
- la colección restante no contiene elementos:
chars.Count() == 0
- o el número de elementos restantes únicos es 1:
chars.Distinct().Count() == 1
- la colección restante no contiene elementos:
Actualización : aquí hay una versión diferente, que no usa LINQ pero aún es legible (IMO). Se implementa como un método y se puede llamar con cualquier número de caracteres que se van a probar:
public bool AllEqualOrZero(params char[] chars)
{
if (chars.Length <= 1) return true;
char? firstNonZero = null;
foreach (var c in chars)
{
if (c != ''0'')
{
firstNonZero = firstNonZero ?? c;
if (c != firstNonZero) return false;
}
}
}
// Usage:
AllEqualOrZero(''0'', ''0'', ''0''); // -> true
AllEqualOrZero(''0'', ''1'', ''1''); // -> true
AllEqualOrZero(''2'', ''1'', ''0''); // -> false
AllEqualOrZero(); // -> true
AllEqualOrZero(''1''); // -> true
Eso no es muy diferente de la respuesta aceptada pero de todos modos
var list = new List<Char> {''1'', ''1'', ''0''};
var check = list.Where(ch => ch != ''0'')
.Distinct()
.Count() < 2;
Podrías escribir una estructura "MYChar" que implementa char
y reemplaza a Equals
, operadores de igualdad y conversión implícita para que puedas hacer:
MyChar a = ''a'';
MyChar b = ''0'';
bool eq = a == b; //true
Editar
Resulta que no se puede heredar de char
porque está sellado, pero probé el siguiente código. Se compila, pero no estoy seguro de que funcione. Lo compilé de http://compilr.com/IDE/34853 , pero no tengo nada que probar en ese momento.
aquí va :
public struct MyChar
{
private static char _wild = ''0'';
private char _theChar;
public MyChar(char c)
{
_theChar = c;
}
public MyChar ()
:this (_wild)
{}
private bool IsWildCard ()
{
return _theChar.Equals (_wild);
}
public static implicit operator char (MyChar c)
{
return c._theChar;
}
public static implicit operator MyChar (char c)
{
return new MyChar (c);
}
public override bool Equals (object obj)
{
if (!(obj is MyChar))
{
return base.Equals (obj);
}
else
{
if (IsWildCard ())
{
return true;
}
else
{
MyChar theChar = (MyChar) obj;
return theChar.IsWildCard () || base.Equals ((char) theChar);
}
}
}
public override int GetHashCode ()
{
return _theChar.GetHashCode ();
}
}
Qué pasa:
if ((a==b) && (b==c) && (a==c))
....
....
¿Está mi lógica equivocada?
Si limita los caracteres a ASCII y no a Unicode, me gusta: http://ideone.com/khacx . (editado en respuesta a un comentario que señalaba que no tenía las especificaciones correctas, pero todavía me gusta la idea básica. Se agregó una prueba adicional como verificación).
using System;
class example {
static void elegant(char a, char b, char c) {
int y = ((int) a - 48) + ((int) b - 48) + ((int) c - 48);
int z = ((int) a - 48) * ((int) b - 48) * ((int) c - 48);
bool result = y == ((int) a-48)*3 || (z ==0 && (a==b || b==c || a==c));
Console.WriteLine(result);
}
static void Main() {
elegant(''0'', ''b'', ''c''); // false
elegant(''a'', ''0'', ''c''); // false
elegant(''a'', ''b'', ''0''); // false
elegant(''a'', ''b'', ''c''); // false
elegant(''0'', ''0'', ''0''); // true
elegant(''a'', ''a'', ''a''); // true
elegant(''0'', ''a'', ''a''); // true
elegant(''a'', ''0'', ''a''); // true
elegant(''a'', ''a'', ''0''); // true
elegant(''0'', ''0'', ''a''); // true
elegant(''0'', ''a'', ''0''); // true
elegant(''a'', ''0'', ''0''); // true
}
}
Para una solución más general que cubre un número ilimitado de caracteres, eso es lo que son las expresiones regulares para: ^ (.) (/ 1 | 0) * $
bool MatchTwo(char a, char b)
{
return a == ''0'' || b == ''0'' || a == b;
}
bool MatchThree(char a, char b, char c)
{
return MatchTwo(a, b) && MatchTwo(a, c) && MatchTwo(b, c);
}
No estoy seguro de que lo llamaría elegante, pero no es horrible (e incluso podría ser correcto ...) (nota, esto es más o menos un refinamiento de la respuesta de Paddy arriba).