2016-09-06 4 views
1

Ich versuche, eine Reihe von Personen zu filtern und die Ergebnisse in einer bestimmten Reihenfolge zu erhalten. Was bedeutet, wenn der Benutzer someString eingibt, möchte ich zuerst die Leute zeigen, deren Name mit dem someString übereinstimmt, und dann mit dem someString und danach jedem Namen beginnen, der diesen String enthält. Irgendwie kann ich es mit Array "Filter" tun oder muss ich eine andere manuelle Sortierung danach?Filtern eines Arrays von Objekten mit Prioritäten

Hier ist mein Filtercode:

self.filteredNames = self.names.filter({$0.name.lowercaseString.hasPrefix(text!) | $0.name.lowercaseString.containsString(text!) | $0.allEmails.lowercaseString.containsString(text!.lowercaseString) | $0.allNumbers.lowercaseString.containsString(text!.lowercaseString)}) 
+1

Ihr Filtercode entspricht nicht Ihrer Beschreibung Ihres Problems. Es sieht so aus, als würden Sie nach verschiedenen Feldern filtern und nicht nach weniger spezifischen Anforderungen für dasselbe Feld. – tebs1200

+0

eigentlich beides. Die Ergebnisse sollten wie angegeben lauten: Namen, die mit der Zeichenfolge übereinstimmen, Namen, die mit dieser Zeichenfolge beginnen, Namen, die diese Zeichenfolge enthalten, E-Mail, die diese Zeichenfolge enthält, Telefonnummern, die diese Zeichenfolge enthalten. – nevgauker

Antwort

1

filter kann nur Elemente aus einer Sammlung entfernen. Sie können keine Bestellvorgänge für Sie ausführen.

Sie möchten wahrscheinlich dies mit einem einzigen Durchlauf des Arrays lösen, so dass es anständig funktioniert. Verwenden Sie keine Filter und stattdessen tun etwas Grundsätzliches wie folgt aus:

var query = "test" 

let allStrings = ["another test", "test", "testing", "random"] 

var matchStrings = [String]() 
var prefixStrings = [String]() 
var containsStrings = [String]() 

for str in allStrings { 

    if query == str { 
     matchStrings.append(str) 
    } else if str.hasPrefix(query) { 
     prefixStrings.append(str) 
    } else if str.containsString(query) { 
     containsStrings.append(str) 
    } 
} 

let results = matchStrings + prefixStrings + containsStrings 
0

Verwendung sort:

func sortingFunction(obj1: YourClass, _ obj2: YourClass) -> Bool { 
    return (str1.name.lowercaseString.hasPrefix(text!) && !obj2.name.lowercaseString.hasPrefix(text!)) || 
     (obj1.name.lowercaseString.containsString(text!) && !obj2.name.lowercaseString.containsString(text!)) || 
     (obj1.allEmails.lowercaseString.containsString(text!) && !obj2.allEmails.lowercaseString.containsString(text!)) && 
     (obj1.allNumbers.lowercaseString.containsString(text!) && !obj2.allNumbers.lowercaseString.containsString(text!)) 
} 

self.filteredNames = self.names.sort(sortingFunction) 
+0

Sie benötigen noch eine Operation, um alle Elemente zu entfernen, die nicht mit der Abfrage übereinstimmen, aber richtig? – tebs1200

+0

richtig, das wird nur das Element sortieren und überhaupt keine entfernen, Ihre Antwort ist dafür geeignet – S2dent

0

Ihr Code nicht mit der Problembeschreibung. Ich gehe mit dem, was du beschrieben hast, da dein Code sowieso nicht das tut, was du willst.

können Sie jedes Spiel ein Tor und sortieren nach dem Abgleichswert:

struct Person { 
    var name: String 
} 

let names = [ 
    Person(name: "John Smith"), 
    Person(name: "Alan Johnson"), 
    Person(name: "Jane Doe"), 
    Person(name: "John") 
] 

let searchText = "John".lowercaseString // take this from you search box 

let filteredNames = names.flatMap { p -> (Person, Int)? in 
    var score = 0 
    let name = p.name.lowercaseString 

    if name == searchText { 
     score += 3 
    } else if name.hasPrefix(searchText) { 
     score += 2 
    } else if name.containsString(searchText) { 
     score += 1 
    } 

    return score == 0 ? nil : (p, score) 
}.sort { $0.1 > $1.1 } 
.map { $0.0 } 

print(filteredNames) // John, John Smith, Alan Johnson 

hier, was es tut:

  • flatMap wirkt als map und filter zugleich. Für jede Person wird basierend auf Ihren Kriterien eine übereinstimmende Punktzahl vergeben. Wenn die übereinstimmende Punktzahl 0 ist, geben wir nil zurück, also flatMap schließt es aus.
  • sort sortiert das Ergebnis durch den Abgleichswert
  • map die passende Partitur entfernen, so dass nur die letzte Reihe der Person

enthalten Sie können diese erweitern, um eine E-Mail, Telefonnummern usw. Gleich um die Punkte im Spiel Mechanismus.

+0

Dieser Ansatz erfordert mehrere Iterationen über die Sammlung, (obwohl zumindest die nachfolgende 'sort' und' map' über a sein wird Teilmenge der Sammlung). Für eine große Sammlung, besonders dort, wo es viele "Hits" gibt, wird eine einzelne "for" -Schleife wahrscheinlich besser funktionieren, oder? – tebs1200

+0

Donald Knuth: "Frühzeitige Optimierung ist die Wurzel aller Übel". Die Leistung hängt von einer Vielzahl von Faktoren ab: (a) Damit Sie wirklich einen Unterschied erkennen können, müssen Hunderte von Tausend Elementen im Array vorhanden sein; (b) Ihre "for" -Schleife iteriert wiederholt, obwohl sie mehrfach vorhanden ist; (c) Wenn Sie "Person" von "struct" in "class" ändern, wird es viel schneller, da es nur die Zeigeradresse kopieren muss, nicht die gesamte Struktur.Verwenden Sie einfach das, was für Sie am sinnvollsten ist, und vergleichen Sie dann die Leistung. –

+0

In Bezug auf (b), wo finden die multiplen Iterationen statt? – tebs1200

Verwandte Themen