NSPredicate in swift

Перевод статьи

Эта страница содержит примеры использования NSPredicate, посмотрите здесь примеры использования Core Data. 🔋

Оглавление

База

Формат предиката и аргументы

Спецификатор формата строки

Основное сравнение

Составное сравнение

Сравнение без учета регистра

Техники

Повторное использование NSPredicate с замещающей переменной

Использование NSPredicate для фильтрации массива объектов

Примеры (собственность сущности…)

Входит в массив значений

Не входит в массив значений

Начинается с определенной строки

Содержит определенную строку

Заканчивается определенной строкой

Подстановочный знак соответствует строке

Регулярное выражение соответствует строке

База

Формат предиката и аргументы

Скажем для предиката, который выбирает человека с именем «Азриэль» и с 50 наличностью:

let fetchRequest = NSFetchRequest<Person>(entityName: "Person")
fetchRequest.predicate = NSPredicate(format: "name == %@ AND money == %i", "Asriel", 50)

формат «name == %@ AND money == %i»

%@ и %i являются спецификаторами формата, %@ заменяется объектом (например, строкой, датой и т. д.), тогда как %i заменяется целым числом.

Замена происходит, как показано ниже, в порядке слева направо:

%@ (описатель формата объекта) будет заменен на «Asriel», а %i (описатель целочисленного формата) будет заменен на 50. Asriel и 50 — это аргументы.

После подстановки предикат станет «name == ‘Asriel’ AND money = 50» , что означает, что NSPredicate найдет для Person имя Asriel и 50 money.

Но почему я не могу просто использовать «имя == ‘Азриэль’ И деньги = 50» вместо того, чтобы использовать спецификатор формата?

Да, конечно ты можешь! Для простого фильтра, который требует жестко заданного значения, я рекомендую использовать его. Замена спецификатора формата обычно используется для значения переменной, например:

// user input a name into textfield
var name = nameTextField.text!
 
// filter based on the name user has inputed
let fetchRequest = NSFetchRequest<Person>(entityName: "Person")
fetchRequest.predicate = NSPredicate(format: "name == %@", name)

Спецификатор формата строки

Вы можете проверить полный список в официальной документации Apple. %@ используется для таких объектов, как String, Date, Array и т. д. %K используется для Keypath (свойство сущности).

let integerPredicate = NSPredicate(format: "money == %i", 10000)
let doublePredicate = NSPredicate(format: "perimeter > %f", 3.14159)
let stringPredicate = NSPredicate(format: "name == %@", "Asriel")
 
// eg: find loans that are overdue
let datePredicate = NSPredicate(format: "due_date < %@", Date())
 
// the above can be replaced with this
let keyPathDatePredicate = NSPredicate(format: "%K < %@", "due_date", Date())

Основное сравнение

Основной символ сравнения, такой как ==, > , < и т. д.

let equalPredicate = NSPredicate(format: "name == %@", "Steve Jobs")
let notEqualPredicate = NSPredicate(format: "name != %@", "Steve Jobs")
 
let greaterPredicate = NSPredicate(format: "money > %i", 10000)
let greaterOrEqualPredicate = NSPredicate(format: "money >= %i", 10000)
 
let lesserPredicate = NSPredicate(format: "money < %i", 10000)
let lesserOrEqualPredicate = NSPredicate(format: "money <= %i", 10000)

Составное сравнение

Соедините два или более условий вместе с помощью OR, AND.

// Retrieve records where all conditions are met
let andPredicate = NSPredicate(format: "name == %@ AND money >= %i", "Steve Jobs", 10000)
 
// Retrieve records as long as one of the condition is met
let orPredicate = NSPredicate(format: "name == %@ OR money >= %i", "Steve Jobs", 10000)

Сравнение без учета регистра

Для сравнения без учета регистра поставьте [c] после символа сравнения.

// Works for "jobs", "Jobs", "jObS"
let caseInsensitivePredicate = NSPredicate(format: "name ==[c] %@", "Jobs")

Техники

Повторное использование NSPredicate с замещающей переменной

Поскольку для анализа строки формата NSPredicate приложению требуется относительно много времени, мы должны попытаться сократить создание нескольких NSPredicate и максимально повторно использовать похожие NSPredicate. Мы можем использовать значение переменной в NSPredicate, обозначенное знаком $, и подставить его значение, вызвав метод withSubstitutionVariables.

// Persons' name : ["Asriel", "Asgore", "Toriel", "Frisk", "Flowey"]
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<Person>(entityName: "Person")
 
let reusablePredicate = NSPredicate(format: "name BEGINSWITH $startingName")
 
// replace $startingName with 'As'
fetchRequest.predicate = reusablePredicate.withSubstitutionVariables(["startingName" : "As"])
 
do {
  people = try context.fetch(fetchRequest)
  // ["Asriel", "Asgore"]
} catch let error as NSError {
  print("Could not fetch. \(error), \(error.userInfo)")
}
 
// reuse the predicate with a different starting name
// replace $startingName with 'F'
fetchRequest.predicate = reusablePredicate.withSubstitutionVariables(["startingName" : "F"])
 
do {
  people += try context.fetch(fetchRequest)
  // ["Asriel", "Asgore", "Flowey", "Frisk"]
} catch let error as NSError {
  print("Could not fetch. \(error), \(error.userInfo)")
}

Вы можете использовать несколько переменных, таких как имя BEGINSWITH $startingName AND money > $amount , а затем вызвать withSubstitutionVariables([«startingName» : «As», «amount»: 50]).

Использование NSPredicate для фильтрации массива объектов

Помимо Core Data, мы также можем использовать NSPredicate для фильтрации массива объектов. SELF в строке формата означает каждый отдельный элемент в массиве.

let names = ["Kim Kardashian", "Kim Jong Un", "Jimmy Kimmel", "Ken"]
var filteredNames : [String] = []
 
// SELF means each element in the array
let containPredicate = NSPredicate(format: "SELF CONTAINS %@", "Kim")
 
filteredNames = names.filter({ name in
    // the evaluate function will return true if the element satisfy the predicate,
    // otherwise false
containPredicate.evaluate(with: name)
})
 
print("\(filteredNames)")
// ["Kim Kardashian", "Kim Jong Un", "Jimmy Kimmel"]


Примеры (собственность сущности…)

Входит в массив значений

let wantedItemIDs = [1, 2, 3, 5, 8, 13, 21]
 
// Retrieve record with item_id which is inside the wantedItemIDs array
let inclusivePredicate = NSPredicate(format: "item_id IN %@", wantedItemIDs)


Не входит в массив значений

let unwantedItemIDs = [1, 2, 3, 5, 8, 13, 21]
 
// Retrieve record with item_id which is not inside the unwantedItemIDs array
let exclusivePredicate = NSPredicate(format: "NOT (item_id IN %@)", unwantedItemIDs)

Начинается с определенной строки

// Works for "Steven Paul Jobs", "Logan Paul"
let containPredicate = NSPredicate(format: "name CONTAINS %@", "Paul")
 
// Works for "Shop1", "shopping", "my shop", "bishop"
let containCaseInsensitivePredicate = NSPredicate(format: "name CONTAINS[c] %@", "shop")
// the [c] means case insensitive match

Содержит определенную строку

// Works for "Steven Paul Jobs", "Logan Paul"
let containPredicate = NSPredicate(format: "name CONTAINS %@", "Paul")
 
// Works for "Shop1", "shopping", "my shop", "bishop"
let containCaseInsensitivePredicate = NSPredicate(format: "name CONTAINS[c] %@", "shop")
// the [c] means case insensitive match

Заканчивается определенной строкой

// Works for "Steve Jobs", "Lisa Jobs"
let endPredicate = NSPredicate(format: "name ENDSWITH %@", "Jobs")
 
// Works for "mundane jobs", "Steve Jobs"
let endCaseInsensitivePredicate = NSPredicate(format: "name ENDSWITH[c] %@", "jobs")
// the [c] means case insensitive match

Подстановочный знак соответствует строке

LIKE используется для сопоставления с подстановочными знаками.

Подстановочный знак используется для соответствия определенному шаблону строки, например: для соответствия img1.png , img10.png и img100.png мы можем использовать имя файла LIKE ‘img*.png’. * означает, что принимается ноль или более символов между img и .png.

let filenameArr = ["img.png", "img1.png", "img2.png", "img10.png", "img100.png", "img200.txt", "img300.csv"]
 
let pngPredicate = NSPredicate(format: "SELF LIKE %@", "img*.png")
 
let imageArr = filenameArr.filter(){ filename in
pngPredicate.evaluate(with: filename)
}
print(imageArr)
// ["img.png", "img1.png", "img2.png", "img10.png", "img100.png"]

Чтобы соответствовать только одному символу, мы можем использовать ? , например: имя файла LIKE ‘img?.png’ будет соответствовать img1.png, но не img10.png, поскольку принимает только один символ между img и .png.

let filenameArr = ["img.png", "img1.png", "img2.png", "img10.png", "img100.png", "img200.txt", "img300.csv"]
 
let singleCharPngPredicate = NSPredicate(format: "SELF LIKE %@", "img?.png")
 
let imageArr2 = filenameArr.filter(){ filename in
singleCharPngPredicate.evaluate(with: filename)
}
print(imageArr2)
// ["img1.png", "img2.png"]

Регулярное выражение соответствует строке

MATCHES используется для сопоставления с регулярным выражением.

Регулярное выражение используется для сопоставления сложных строковых шаблонов. Swift использует формат регулярных выражений ICU. Для изучения регулярных выражений я рекомендую это руководство.

Например: имя файла СОВПАДАЕТ ‘img\\d{1,3}\\.png‘ будет соответствовать имени файла с 1-3 цифрами между img и .png, например img1.png, img10.png и img100.png, но не img1000.png. Двойная обратная косая черта используется для экранирования символа обратной косой черты \ .

let filenameArr = ["img.png", "img1.png", "imgABC.png", "img10.png", "img100.png", "img9000.png", "img12345.png"]
 
// matches filename that has 1-3 digits between 'img' and '.png'
let regexPredicate = NSPredicate(format: "SELF MATCHES %@", "img\\d{1,3}\\.png")
 
let filteredArr = filenameArr.filter(){ filename in
    regexPredicate.evaluate(with: filename)
}
print(filteredArr)
// ["img1.png", "img10.png", "img100.png"]