2016-03-28 21 views
0

Ich habe eine Filtermethode, die eine generische Array von Strukturen, führt eine Abfrage und/oder Orderby und/oder nehmen Ausdrücke als Zeichenfolgen mit dynamischen Linq, und gibt eine Liste der übereinstimmenden Indizes , die dann von einem externen Programm verwendet wird.Return-Index-Liste von Dynamic Linq Abfrage

ich angenommen zunächst ein bisschen ein notdürftig Ansatz, um die Indexliste bekommen, indem Sie einfach über die ursprüngliche Liste iterieren und zu finden, wo das Element zu einem Element in der gefilterten Liste gleich war wie folgt:

public static int[] FilterStructs<T>(IList<T> structs, string query = "", string orderBy = "", int topN = 0) where T : struct {    
     var filteredStructs = structs.AsQueryable(); 
     if (!string.IsNullOrEmpty(query)) filteredStructs = filteredStructs.Where(query); 
     if (!string.IsNullOrEmpty(orderBy)) filteredStructs = filteredStructs.OrderBy(orderBy); 
     if (topN > 0) filteredStructs = filteredStructs.Take(topN); 
     return GetArrayIndexList(structs, filteredStructs.ToArray()); 
    } 

    private static int[] GetArrayIndexList<T>(IList<T> arrMain, T[] arrFiltered) where T : struct { 
     List<int> indexes = new List<int>(); 
     for (int i = 0; i < arrFiltered.Length; i++) { 
      for (int j = 0; j < arrMain.Count; j++) { 
       if (arrMain[j].Equals(arrFiltered[i])) { 
        indexes.Add(j); 
        break; 
       } 
      } 
     } 
     return indexes.ToArray(); 
    } 

Wenn jedoch das Array von Strukturen sogar ein paar tausend Elemente enthält, wird dies schrecklich langsam.

Was ich im Idealfall tun möchte, ist zunächst eine dynamische Select-Anweisung zu verwenden, um das Array von Strukturen in ein neues Array mit einem zusätzlichen "Index" -Feld zu projizieren, die Filterung durchzuführen und dann nur diese Indexwerte zurückzuliefern Eine Select-Anweisung wäre trivial.

Allerdings zeichne ich ein wenig leer, wie dies erreicht werden könnte.

Jeder Rat wird geschätzt.

Antwort

0

Es war einfacher, als ich dachte, obwohl dies nicht meine ideale Lösung ist, weil ich jeden eingehenden String-Ausdruck für die Abfrage und orderby Klauseln ändern, muß die projizierte Entitätsname umfassen. Aber es funktioniert, bis ich mehr Zeit habe.

public static int[] FilterStructs<T>(IList<T> structs, string query = "", string orderBy = "", int topN = 0) where T : struct { 
     var withIndex = structs.Select((s, i) => new { index = i, item = s }); 
     var filteredStructs = withIndex.AsQueryable(); 
     if (!string.IsNullOrEmpty(query)) filteredStructs = filteredStructs.Where(query); 
     if (!string.IsNullOrEmpty(orderBy)) filteredStructs = filteredStructs.OrderBy(orderBy); 
     if (topN > 0) filteredStructs = filteredStructs.Take(topN); 
     return filteredStructs.Select("index").Cast<int>().ToArray();    
    } 

Zum Beispiel, wenn meine frühere where-Klausel "value = 123" war, hat es jetzt worden "item.value = 123".

+0

Übrigens hat dies die Ausführungszeit von der Verwendung der GetArrayIndexList-Methode von etwa 1500ms auf 40ms reduziert. – Hoodlum

0

Wenn ich richtig verstanden habe, sind die gefilterten Array-Elemente garantiert im ursprünglichen Array vorhanden, so dass Sie den Body GetArrayIndexList durch Folgendes ersetzen können.

private static int[] GetArrayIndexList<T>(IList<T> arrMain, T[] arrFiltered) where T : struct 
    { 
     return arrFiltered.Select(c=> arrMain.IndexOf(c)).ToArray(); 
    } 
+0

Danke dafür. Ich denke jedoch, dass IndexOf den standardmäßigen Gleichheitsvergleich verwenden wird, was dazu führte, dass die Methode langsam war. Obwohl dies viel weniger Code ist, dauert es immer noch mehr als 1 Sekunde für ein Array mit 4000 Elementen. – Hoodlum