Desenvolvimento

5 ago, 2011

Filtrando arrays com NSPredicate

Publicidade

Acho que minha primeira experiência usando a classe NSPredicate no iPhone foi com queries principais de dados, mas ela tem muitas outras aplicações úteis. Eu tive a necessidade, outro dia, de processar uma lista de filenames e os combinar contra uma simples regular expression. O código para criar o array de filenames que eu precisava processar se parecia mais ou menos com isto:

    NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *defaultPath = [[NSBundle mainBundle] resourcePath];
NSError *error;

NSArray *directoryContents = [fileManager contentsOfDirectoryAtPath:defaultPath error:&error];

As primeiras linhas apenas configuram a busca do diretório fonte na aplicação principal, de modo que o directoryContents do array acabe contendo os filenames de todos os arquivos no diretório. Eu então precisava processar aquele array e puxar os filenames que correspondiam à minha regular expression pré-definida.

Strings no formato NSPredicate 

A classe NSPredicate fornece uma maneira geral de especificar uma query que pode ser aplicada para filtrar um array. A atual query a ser utilizada é especificada ao criar o predicado como um argumento para o método predicateWithFormat: Alguns exemplos irão facilitar o seu entendimento. 

Correspondência simples

O exemplo mais simples é quando você somente precisa de uma correspondência exata contra um único valor. Isso é sem sentido e extremamente simples nesse caso, mas ilustra bem a técnica básica:

    NSString *match = @"imagexyz-999.png";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF == %@", match];
NSArray *results = [directoryContents filteredArrayUsingPredicate:predicate];

A string query é simplesmente “SELF == %@”, onde SELF se refere a cada elemento no array. Para usar o predicado, o aplicamos no array dos filenames usando  filterArrayUsingPredicate:. Nesse exemplo trivial (e sem sentido), os arrays resultantes conteriam uma única string “imagexyz-999.png” pressupondo que aquele arquivo existia no diretório. 

Correspondência curinga

Para combinar usando uma string curinga, a querie pode usar like ao invés do operador de comparação e incluir o “*” (correspondente a zero ou mais caracteres) ou “?” (correspondente exatamente a 1 caractere) como curinga:

    NSString *match = @"imagexyz*.png";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF like %@", match];
NSArray *results = [directoryContents filteredArrayUsingPredicate:predicate];

Neste caso as arrays resultantes conteriam os que correspondessem a imagexyz*.png (p.e. imagexyz-001.png, imagexyz-002.png, etc.).

Correspondência sem distinção de maiúscula/minúscula

Para deixar o exemplo anterior um caso de correspondência sem distinção de letras maiúsculas/minúscula, modifique o operador like para like[c]. Existe também a opção adicional de ignorar caracteres acentuados usando like[d]. Você pode combinar essas duas opções assim:

    NSString *match = @"imagexyz*.png";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF like[cd] %@", match];
NSArray *results = [directoryContents filteredArrayUsingPredicate:predicate];

Outras comparações de strings

Além do like, você pode utilizar beginswith, endswith, contains e matches. Eles são em sua maior parte autodidáticos com a exceção, talvez, de matches. Com matches, você consegue especificar uma regular expression completa, o que permite combinações mais complexas do que é possível os caracteres curingas *e ?. Por exemplo, para combinar filenames da forma imagexyz-ddd.png, onde ddd são dígitos:

    NSString *match = @"imagexyz-d{3}.png";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF matches %@", match];
NSArray *results = [directoryContents filteredArrayUsingPredicate:predicate];

Não tentaria explicar a sintaxe de regular expression aqui, mas note que o backslash () é escapado ao usar um backslash duplo. Note também que strict ICU regular expression formats são usadas.

*

Artigo original publicado em http://useyourloaf.com/blog/2010/7/27/filtering-arrays-with-nspredicate.html