2011-10-19 15 views
12

Tengo un Dictionary<string, List<int>> en mi código que estoy usando la siguiente manera:¿Es posible hacer una coincidencia de cadena parcial en una clave de cadena de diccionario?

Key   Values 
2011-07-15 1, 2, 3 
2011-07-20 4, 5, 6 
2010-02-11 7, 8, 9 

Mi código tiene que ser capaz de consultar para todos los valores que coincidan con una subserie particular, en la clave. Por ejemplo, si tuviera la subcadena 2011-07, debería devolver los valores {1, 2, 3, 4, 5, 6}. Una subcadena de 11 debe devolver todos los ID de 1-9.

¿Alguien puede recomendar una forma concisa para lograr esto? ¿O proporcionar una mejor estructura de datos para recuperar esta información?

+0

Creo que la variedad de respuestas a continuación muestra que las personas están asumiendo cosas diferentes para lo que significa "subcadena". Supongo por su comentario sobre 11 que quiere decir una subcadena realmente general que no es necesariamente un prefijo, sufijo, solo un [año | mes | día], y no es una expresión regular? –

+1

@J Trana - tienes razón, quise decir una subcadena realmente general. – LeopardSkinPillBoxHat

+0

@LeopardSkinPillBoxHat, ¿podría publicar la solución definitiva que aprobó? – EndlessSpace

Respuesta

8

lo haría un método de extensión:

public static class DictionaryExt 
{ 
    public static IEnumerable<T> PartialMatch<T>(this Dictionary<string, T> dictionary, string partialKey) 
    { 
     // This, or use a RegEx or whatever. 
     IEnumerable<string> fullMatchingKeys = 
      dictionary.Keys.Where(currentKey => currentKey.Contains(partialKey)); 

     List<T> returnedValues = new List<T>(); 

     foreach (string currentKey in fullMatchingKeys) 
     { 
      returnedValues.Add(dictionary[currentKey]); 
     } 

     return returnedValues; 
    } 
} 

El "costo" de la adición de los valores al diccionario no cambiaría, pero el costo de la recuperación sería mayor, pero sólo cuando se que estas yendo con una coincidencia parcial.

Btw, estoy seguro de que podría transformar esto en una sola expresión Lambda, pero el concepto sigue siendo el mismo.

Editar: En su ejemplo, este método devolverá 2 listas de valores, pero puede cambiarlo para combinar las listas. Aquí está el método de extensión que podría hacer:

public static IEnumerable<T> PartialMatch<T>(
    this Dictionary<string, IEnumerable<T>> dictionary, 
    string partialKey) 
{ 
    // This, or use a RegEx or whatever. 
    IEnumerable<string> fullMatchingKeys = 
     dictionary.Keys.Where(currentKey => currentKey.Contains(partialKey)); 

    List<T> returnedValues = new List<T>(); 

    foreach (string currentKey in fullMatchingKeys) 
    { 
     returnedValues.AddRange(dictionary[currentKey]); 
    } 

    return returnedValues; 
} 

Editar 2: Ahora que lo pienso de ella, también podría hacer más genérico. Con el siguiente método de extensión, que funcionaría en cualquier diccionario, siempre y cuando proporcione una comparer que comprueba lo que entendemos por "coincidencia parcial":

public static IEnumerable<TValue> PartialMatch<TKey, TValue>(
    this Dictionary<TKey, IEnumerable<TValue>> dictionary, 
    TKey partialKey, 
    Func<TKey, TKey, bool> comparer) 
{ 
    // This, or use a RegEx or whatever. 
    IEnumerable<TKey> fullMatchingKeys = 
     dictionary.Keys.Where(currentKey => comparer(partialKey, currentKey)); 

    List<TValue> returnedValues = new List<TValue>(); 

    foreach (TKey currentKey in fullMatchingKeys) 
    { 
     returnedValues.AddRange(dictionary[currentKey]); 
    } 

    return returnedValues; 
} 
+0

este es un buen enfoque. – DarthVader

+0

Con la segunda edición, siempre que apruebe un método de comparación adecuado, podría tener un tipo de clave de diccionario de int, y decir que 43 coincide parcialmente con 343756. – Tipx

1

Una forma concisa sería usar Mapa Multivalue.

Por ejemplo:

Dictionary<string, Dictionary<string, List<int>> 

¿Por qué no almacenar la 2011-07 como una clave y 15 para la llave diccionario interior y 1,2,3 como valores.

map ["2011-07"] ["15"] = {1,2,3};

si quieres solo 2011-07 puedes obtener todo lo que hay en el otro diccionario por traversal.

map["2011-07"] // u sería volver 1,2,3,4,5,6

y si quieres ir a un día específico, 2011-07-15, esto u devolver sólo 1,2,3

foreach(var element in map["2011-07"]){ 

    var values = element.values; // and you can append them to a list. 

} 

si necesita año/mes/día, necesitará diccionarios de varios niveles. o puede usar un árbol también.

+0

¿Puede proporcionar o señalar un ejemplo? – LeopardSkinPillBoxHat

+0

Su propuesta no ayuda con mi segundo ejemplo: deseo poder proporcionar cualquier subcadena; no necesariamente tiene que estar claramente rota en el año/mes/día. – LeopardSkinPillBoxHat

+0

ver la edición. lo siento. – DarthVader

2

Si el diccionario usa hashes internos, no tiene suerte, ya que cadenas similares producen valores hash diferentes. Acabo de implementar una solución a este requisito durante el fin de semana en C, una prueba de entrevista/tarea. Usé una matriz ordenada como la estructura subyacente: inserciones costosas, pero búsquedas rápidas (usando búsqueda binaria). Para encontrar todas las entradas con la clave comenzando con un prefijo, encontraría la 1ª, luego, pasaré la siguiente, siguiente ... Para la subcadena general, es decir, no solo el prefijo, mi solución no funcionaría. En este momento no sé qué sugerir para la búsqueda de "subcadena general".

2

Puede tener tres diccionarios. Año mes dia.

Tenga en cuenta que cuando agrega elementos a tres diccionarios, NO está duplicando los elementos.

Al extraer elementos utilizando dos teclas, puede usar el método de extensión LINQ Intersecar() para obtener los elementos que coincidan con ambas teclas (Usar Intersecar en los dos conjuntos de resultados).

Advertencia, hacerlo de esta manera no daría como resultado el código de ejecución más rápido.

+1

creo que usar un árbol sería más rápido que esto. si comparte los nodos secundarios, entonces no tiene que hacer lo mismo. – DarthVader

+0

¿No tendría que atravesar el árbol varias veces para recoger los nodos correspondientes? Eso tiene que sumarse. –

3

Usted está buscando respuestas concisas. Sin indexación elegante en un nivel bajo para texto (del cual no conozco ninguna clase .Net especializada), creo que el diccionario sigue siendo su mejor apuesta. Consulta con algo como:

myDictionary.Where (kvp => kvp.Key.Contains ("11")). SelectMany (kvp => kvp.Value);

Tienes que buscar todas las claves para una subcadena generalizada de todos modos sin alguna magia genial (no proporcionada por .Net), por lo que LINQ no debería hacerte mucho daño aquí.

Cuestiones relacionadas