What is SUBQUERY?

What the heck is SUBQUERY?

One of the lesser known bits of NSPredicate is the SUBQUERY() function. The documentation for a subquery expression explains a little bit about what’s going on, but it takes a while to understand when it’s really useful.

Let’s break it down.

The Syntax

A subquery expression takes 3 arguments:

  1. A collection
  2. A variable name
  3. A predicate

The Collection

The collection can be one of the two standard Cocoa collection: NSArray and NSSet (or some subclass of them). This collection can be hard-coded in, or it can be the result of another expression (like a keyPath expression or the like). A hard-coded collection would look like:

SUBQUERY(%@, ...

A keyPath expression would look like:

SUBQUERY(contents, ...

With the stipulation that [self contents] (or self.contents if you prefer) must return an NSArray or NSSet.

The Variable

The SUBQUERY is going to iterate over the collection, gathering certain objects. We need a way to represent what each item in the collection is, and for that we use the variable.

Variables in an NSExpression (or NSPredicate format string) take the form $identifier, where identifier is a valid C-style identifier. Most examples of SUBQUERY generally use $x as the variable. It’s short and to-the-point.

It’s the second argument in the expression:

SUBQUERY(contents, $x, ...

The Predicate

A predicate, as we know, is a statement that evaluates to true or false. In the case of the subquery, the predicate will be evaluated for each object (represented by the variable) in the collection. If the predicate returns true, then that object will be included as part of the resulting collection.

SUBQUERY(contents, $x, $x.property = 'foo' and $x.number = 42)

TL;DR

SUBQUERY() expression is the functional equivalent of doing this:

NSPredicate * p = [NSPredicate predicateWithFormat:@"property = 'foo' and number = 4"];
NSArray * results = [[self contents] filteredArrayUsingPredicate:p];

If this were expressed as a subquery in a predicate, it would be:

NSPredicate * p = [NSPredicate predicateWithFormat:@"SUBQUERY(contents, $x, $x.property = 'foo' and $x.number = 42) ..."];  
//with some operation to use the resulting collection in a comparison or something

So what?

Now that we get what the various bits of a subquery expression are, let’s ask the real question: when is this ever useful?

To be honest, the answer to this is “not often”. However, when you need it, it’s incredibly useful.

Rule of thumb

The general rule of thumb on when you should consider using SUBQUERY is this:

If you have a collection (A) of objects, and each object has a collection (B) of other objects, and you’re trying to filter A based on some varying attributes (at least 2) of the objects in B, then you should probably be using SUBQUERY.

Example

Let’s say you have a bunch of Project objects, and each Project has a bunch of ToDo items. A ToDo item has a completionDate (anNSDate) and a user (a name). You want to find all projects that have a todo item that was completed by Joey (so completionDate is notnil and user is “Joey”). We’re going to display these in a “Joey’s Recent Projects” group (or something).

Our first reaction might be a predicate that uses ANY in there, like:

ANY todos.completionDate != nil AND ANY todos.user == joey

Unfortunately, that would give us projects that have at least one completed ToDo and that has a ToDo whose user is Joey. However, they don’t have to be the same ToDo.

The proper predicate is:

SUBQUERY(todos, $todo, $todo.completionDate != nil AND $todo.user = 'Joey').@count > 0

This predicate will be evaluated against each Project. First, we’ll get the collection of ToDo objects by evaluating the todo keyPath on the Project. Then for each item ($todo) in the array of ToDo objects, we’re going to check and see if that object’s completionDate is non-nil and if that object’s user is "Joey". If that’s true, then it’ll be added to the resulting collection.

When the SUBQUERY completes, we’ll have an array of ToDo items that were completed by Joey. At this point, we retrieve the number of items in that collection (via the @count keyPath) and see if it’s greater than 0. If it is, then the corresponding Project will be added to the final array. In this manner, we can retrieve all Projects that have ToDos completed by Joey.

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在 IT 领域,文档格式转换是常见需求,尤其在处理多种文件类型时。本文将聚焦于利用 Java 技术栈,尤其是 Apache POI 和 iTextPDF 库,实现 doc、xls(涵盖 Excel 2003 及 Excel 2007+)以及 txt、图片等格式文件向 PDF 的转换,并实现在线浏览功能。 先从 Apache POI 说起,它是一个强大的 Java 库,专注于处理 Microsoft Office 格式文件,比如 doc 和 xls。Apache POI 提供了 HSSF 和 XSSF 两个 API,其中 HSSF 用于读写老版本的 BIFF8 格式(Excel 97-2003),XSSF 则针对新的 XML 格式(Excel 2007+)。这两个 API 均具备读取和写入工作表、单元格、公式、样式等功能。读取 Excel 文件时,可通过创建 HSSFWorkbook 或 XSSFWorkbook 对象来打开相应格式的文件,进而遍历工作簿中的每个 Sheet,获取行和列数据。写入 Excel 文件时,创建新的 Workbook 对象,添加 Sheet、Row 和 Cell,即可构建新 Excel 文件。 再看 iTextPDF,它是一个用于生成和修改 PDF 文档的 Java 库,拥有丰富的 API。创建 PDF 文档时,借助 Document 对象,可定义页面尺寸、边距等属性来定制 PDF 外观。添加内容方面,可使用 Paragraph、List、Table 等元素将文本、列表和表格加入 PDF,图片可通过 Image 类加载插入。iTextPDF 支持多种字体和样式,可设置文本颜色、大小、样式等。此外,iTextPDF 的 TextRenderer 类能将 HTML、
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值