El problema:
Nuevamente, este "truco" viene a raíz de una pregunta en mis foros, en la
que se decía que como se puede buscar (en una lista, colección, base de datos,
etc.) de forma que si no se encuentra lo buscado, se devuelva el que esté
anterior en el orden, por ejemplo, si tenemos estas palabras: Al, Alameda y
Altanero, y buscamos Almacen, que si no está, que devuelva Alameda, que es la
que está antes de donde debería estar Almacen.
La solución:
Si esto lo hacemos con LINQ podríamos buscar la palabra indicada, y en
caso de que no exista, hacer una nueva búsqueda, pero esta vez buscando las
que sean menores, es decir, estén antes en orden alfabético (ni que decir
tiene que esto sólo funcionará si la lista en la que buscamos está
clasificada).
Teniendo la colección del tipo Cliente que ya vimos en el truco de cómo "Buscar datos con LINQ sin tener en cuenta mayúsculas/minúsculas",
podríamos hacer algo como lo que te muestro un poco más abajo.
Nota:
En este ejemplo se utiliza una colección generic del tipo List(Of Cliente) o
List<Cliente>, estos links te llevarán al código para crear esa colección,
tanto para Visual Basic como para
C#.
Debido a que se utilizan instrucciones de LINQ, este código solamente es
válido para versiones de .NET Framework igual o superior a la 3.5.
En el caso de la clase Cliente para Visual Basic sólo es válido para la
versión 2010, salvo que definas las propiedades de forma "habitual".
en Visual Basic:
' Buscar el anterior más parecido al indicado (si no está el buscado)
Private Sub consultaParecida(ByVal txtConsulta As String)
Console.WriteLine("Buscando: {0}", txtConsulta)
' Puede que lo usemos varias veces
Dim conLower = txtConsulta.ToLower()
Dim resp = From cli In clis _
Where cli.Apellidos.ToLower = conLower _
Select cli
If resp.Count = 0 Then
' Buscar el anterior
resp = From cli In clis _
Where cli.Apellidos.ToLower < conLower _
Select cli
' Si se quiere busca sólo los que empiecen con la misma letra
resp = From cli In clis _
Let apeLower = cli.Apellidos.ToLower
Where apeLower.StartsWith(conLower.Substring(0, 1)) _
AndAlso apeLower < conLower _
Select cli
End If
If resp.Count > 0 Then
Dim cli2 = resp.Last
Console.WriteLine("{0}, {1}", cli2.Apellidos, cli2.DNI)
Else
Console.WriteLine("No se ha encontrado nada")
End If
End Sub
en C#:
// Buscar el anterior más parecido al indicado (si no está el buscado)
static void consultaParecida(string txtConsulta)
{
Console.WriteLine("Buscando: {0}", txtConsulta);
// Puede que lo usemos varias veces
var conLower = txtConsulta.ToLower();
var resp = from cli in clis
where cli.Apellidos.ToLower() == conLower
select cli;
// Count es un método...
if (resp.Count() == 0)
{
// Nota:
// Ya no lo recordaba (ya que supongo que lo sabría)
// de que en C# no están sobrecargados los operadores < ni >
// para la clase String.
// Por tanto, hay que usar string.Compare (o CompareTo)
//
// Buscar el anterior
resp = from cli in clis
where string.Compare(cli.Apellidos.ToLower(), conLower) < 0
select cli;
// Si se quiere busca sólo los que empiecen con la misma letra
resp = from cli in clis
let apeLower = cli.Apellidos.ToLower()
where apeLower.StartsWith(conLower.Substring(0, 1))
&& apeLower.CompareTo(conLower) < 0
select cli;
}
if (resp.Count() > 0)
{
var cli2 = resp.Last();
Console.WriteLine("{0}, {1}", cli2.Apellidos, cli2.DNI);
}
else
{
Console.WriteLine("No se ha encontrado nada");
}
}
El truco está en usar el método extensor Last para
acceder al último elemento que hemos obtenido, en el caso de que encontremos
lo que buscamos, el último será el que buscamos; y en caso de que se cumpla
la segunda consulta de LINQ, el último será precisamente el que tendría que
estar antes del que buscamos.
Espero que te sea de utilidad.
Nos vemos.
Guillermo
Espacios de nombres usados en el código de este artículo:
System.Linq