NSPredicate Filtered childs on an array

Good afternoon, I'm using the new Apple Swift language and using NSArray:

[
    {
        "Section" : "Title1",
        "Items" :
        [
            {"Nr" : "101"},
            {"Nr" : "201"},
            {"Nr" : "301"},
            {"Nr" : "401"}
        ]
    },
    {
        "Section" : "Title2",
        "Items" :
        [
            {"Nr" : "102"},
            {"Nr" : "202"},
            {"Nr" : "302"}
        ]
    },
    {
        "Section" : "Title3",
        "Items" :
        [
            {"Nr" : "1102"},
            {"Nr" : "2102"},
            {"Nr" : "3102"}
        ]
    }
]

      

I want to use search and only display the content found

As I understand it, I need to use "filterArrayUsingPredicate" and NSPredicate in swift, so my example is:

var arr:NSArray = [...] // My array sample goes here
var pre:NSPredicate = NSPredicate(format: "ANY Items.Nr BEGINSWITH[c] %@", argumentArray: ["20"]) // We a going to search "20" in /Items/Nr

var result:NSArray = arr.filteredArrayUsingPredicate(pre)

      

This works correctly, but the result is not what I need:

result = 
[
    {
        "Section" : "Title1",
        "Items" :
        [
            {"Nr" : "101"},
            {"Nr" : "201"},
            {"Nr" : "301"},
            {"Nr" : "401"}
        ]
    },
    {
        "Section" : "Title2",
        "Items" :
        [
            {"Nr" : "102"},
            {"Nr" : "202"},
            {"Nr" : "302"}
        ]
    }
]

      

Shows me the whole section which contains Imtes with Nr starting with "20"

And my question is how to make the filter on the elements as well? As a result, I will need:

result = 
[
    {
        "Section" : "Title1",
        "Items" :
        [
            {"Nr" : "201"}
        ]
    },
    {
        "Section" : "Title2",
        "Items" :
        [
            {"Nr" : "202"}
        ]
    }
]

      

I tried using SUBQUERY:

"SUBQUERY(Items, $x, $x.Nr BEGINSWITH[c] %@).@count > 0"

      

But this will return me the same, if it can calculate the Items, then I thought it could display me Items / Nr, which I found, but I don't know how to spell it correctly. :)

+3


source to share


1 answer


Oh hold on. I'll just re-read your question, now editing

To answer the requirement to filter the child arrays. Your data model is getting too complex to use shared objects. You should consider customizing the actual data structure with custom objects. You can then offload most of the work to these objects, instead of putting all the work in one place.

Just try to find the best way to do it (no special data model).

PS create a custom data model :)

Ok here we go

I made it much easier by wrapping your shared data in a structure ...

struct Item {
    let section: String
    let items: [[String: String]]
}

      

It still seems wrong to have an array of dictionaries, where each dictionary only contains one key, and it's always the same. In fact, it should be an array of strings.

Anyway...

Create an array like this ...

let items: [Item] = [
    Item(section: "Title1", items: [["Nr" : "101"], ["Nr" : "201"], ["Nr" : "301"], ["Nr" : "401"]]),
    Item(section: "Title2", items: [["Nr" : "102"], ["Nr" : "202"], ["Nr" : "302"]]),
    Item(section: "Title3", items: [["Nr" : "1102"], ["Nr" : "2102"], ["Nr" : "3102"]])
]

      

Or from your data, however you saved it.

Then filter and move it to a new array ...



let filteredMappedArray = items.filter {
    // this filters the array so that only its containing "20..." are in it.
    for dictionary in $0.items {
        if let numberString = dictionary["Nr"] {
            if (numberString.hasPrefix("20")) {
                return true
            }
        }
    }
    return false
}.map {
    // This maps the array and removes anything that isn't "20..." from the items.
    Item(section: $0.section, items: $0.items.filter {
        numberDictionary in
        if let numberString = numberDictionary["Nr"] {
            return numberString.hasPrefix("20")
        }
        return false
    })
}

      

Then I wrote down the result ...

for item in filteredMappedArray {
    println("Section: \(item.section) - Items: \(item.items)")
}

      

And got this result ...

Section: Title1 - Items: [[Nr: 201]]
Section: Title2 - Items: [[Nr: 202]]

      

There may be a better / easier way to combine everything into one function, but this is the easiest way to find.

If you can change your dictionary array ...

If an element can be defined as ...

struct Item {
    let section: String
    let items: [String]
}

      

Then the filter-filter function becomes ...

let filteredMappedArray = items.filter {
    // this filters the array so that only its containing "20..." are in it.
    for numberString in $0.items {
        if (numberString.hasPrefix("20")) {
            return true
        }
    }
    return false
}.map {
    // This maps the array and removes anything that isn't "20..." from the items.
    Item(section: $0.section, items: $0.items.filter {$0.hasPrefix("20")})
}

      

By changing this dictionary array to just an array of strings, you remove a level of complexity you don't need.

+1


source







All Articles