NSP-предикат с условием, основанным на рекурсивных отношениях - Swift - Основные данные - PullRequest
0 голосов
/ 06 февраля 2019

Я использую Core Data, и у меня есть базовая структура папок.Каждый элемент (файл / папка) представляет собой строку в таблице.Каждый элемент имеет свойство name, а также необязательное отношение, которое указывает на элемент, который является его родителем.Я пытаюсь написать функцию, которая принимает массив строк, представляющих путь (например, ["myFolder", "mySubfolder", "mySubSubfolder", "myFile"]), и возвращает элемент, найденный по этому пути.На английском языке я хотел бы спросить «Основные данные»: «Дайте мне все предметы, имя которых myFile, имя родителя mySubSubfolder, имя деда mySubfolder, а имя прадеда myFolder».,Гораздо более простой способ описать это словами - определить условие под названием item(at: path), которое говорит Core Data «дать мне все элементы, чье имя path.last и чей родитель удовлетворяет item(at: path.dropLast())».В моем сердце есть надежда, что есть причудливая особенность предикатов, которая делает это возможным, но я достаточно новичок для них, что у меня возникают проблемы с поиском ответа.

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

Мне нравится использовать строго типизированные методы, где это возможно, вместо создания и анализа строк формата во время выполнения.

import Foundation

func predicateMatchingPath(_ path: [String]) -> NSPredicate {
    var path = path
    guard let last = path.popLast() else {
        return NSPredicate(value: false)
    }

    var ancestorKeyPath = ""
    let nameSuffix = "name"
    var predicates = [NSPredicate]()

    predicates.append(NSComparisonPredicate(
        leftExpression: NSExpression(forKeyPath: ancestorKeyPath + nameSuffix),
        rightExpression: NSExpression(forConstantValue: last),
        modifier: .direct, type: .equalTo, options: []))

    while let anscestor = path.popLast() {
        ancestorKeyPath.append("parent.")
        predicates.append(NSComparisonPredicate(
            leftExpression: NSExpression(forKeyPath: ancestorKeyPath + nameSuffix),
            rightExpression: NSExpression(forConstantValue: anscestor),
            modifier: .direct, type: .equalTo, options: []))
    }

    // Make sure the most distant ancestor is a root.
    predicates.append(NSComparisonPredicate(
        leftExpression: NSExpression(forKeyPath: ancestorKeyPath + "parent"),
        rightExpression: NSExpression(forConstantValue: nil),
        modifier: .direct, type: .equalTo, options: []))

    return NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
}

print(predicateMatchingPath(["myFolder", "mySubfolder", "mySubSubfolder", "myFile"]))

// Output:
name == "myFile" AND parent.name == "mySubSubfolder" AND parent.parent.name == "mySubfolder" AND parent.parent.parent.name == "myFolder" AND parent.parent.parent.parent == nil
0 голосов
/ 06 февраля 2019

Хорошо, на самом деле, я думаю, я понял это.На самом деле еще не проверял, но концептуально это кажется правильным.Вот код, который создает правильную строку предиката:

var predicateString = ""
var i = 0
for pathComponent in path.reversed() {
    predicateString += "name == \(pathComponent) && parent"
    for _ in 0 ..< i {
        predicateString += ".parent"
    }
    i += 1
    guard i != path.count else { continue }
    predicateString += "."
}
predicateString += " == nil"

...