Selecting and Filtering Collections
This is the densest chapter in this section, and arguably the most useful — select, reject, selectattr, rejectattr, and map are what “complex data manipulation” actually looks like in practice: filtering a list down to what you care about, then pulling out just the piece you need.
Note
A few of these filters take a word like 'even' or 'equalto' as an argument — these are called Jinja2 tests, and they get a proper, full treatment in the next section, alongside when:. For this chapter, treat them as ready-made comparison keywords these filters accept, without worrying about the broader concept yet.
select And reject: Filtering A List Of Values
select keeps only the items in a list that pass a given test:
vars:
numbers: [1, 2, 3, 4, 5, 6]
tasks:
- name: Select even numbers
ansible.builtin.debug:
msg: "{{ numbers | select('even') | list }}"[2, 4, 6]reject keeps the opposite — everything that fails the test:
msg: "{{ numbers | reject('even') | list }}"[1, 3, 5]Note the trailing | list on both — like zip and dict2items | keys() from earlier chapters, these filters produce something that needs to be explicitly materialized into a real list.
selectattr And rejectattr: Filtering A List Of Dictionaries
select/reject work on plain values. selectattr/rejectattr do the same thing for a list of dictionaries, testing one specific key in each:
vars:
servers:
- name: web1
status: running
- name: web2
status: stopped
- name: db1
status: running
tasks:
- name: Select running servers
ansible.builtin.debug:
msg: "{{ servers | selectattr('status', 'equalto', 'running') | list }}"[{"name": "web1", "status": "running"}, {"name": "db1", "status": "running"}]msg: "{{ servers | rejectattr('status', 'equalto', 'running') | list }}"[{"name": "web2", "status": "stopped"}]map: Transforming Every Item In A List
map applies something to every item in a list at once. Its most common form pulls a single key out of a list of dictionaries:
msg: "{{ servers | map(attribute='name') | list }}"["web1", "web2", "db1"]It also works with an ordinary filter name, applied to every item:
msg: "{{ ['hello', 'world'] | map('upper') | list }}"["HELLO", "WORLD"]Putting It Together: selectattr Then map
This combination is where the real value shows up — filter down to what you want, then extract just the piece you actually need:
msg: "{{ servers | selectattr('status', 'equalto', 'running') | map(attribute='name') | list }}"["web1", "db1"]One line: “give me the names of every running server.” This kind of chained filter pipeline is exactly what this section has been building toward — reshaping a raw data structure into exactly the shape a later task actually needs.
A Gotcha: length Right After A Filter Might Fail
msg: "{{ numbers | select('even') | length }}"This might raises an error — something like object of type 'generator' has no len(). select (and reject, selectattr, rejectattr) hand back a generator, not a concrete list, and generators don’t support length directly. The fix is the same | list you’ve been chaining onto these filters already, placed before | length:
msg: "{{ numbers | select('even') | list | length }}"3Best Practices
- Chain
| listafterselect/reject/selectattr/rejectattrbefore using the result for anything beyond immediately displaying it — counting it, indexing it, or passing it to a module parameter all need a real list, not a generator. - Combine
selectattrwithmap(attribute=...)for the extremely common “filter, then extract one field” pattern — it’s one line instead of a much more awkward manual approach. - Don’t worry about fully understanding test names like
'even'and'equalto'yet — they’re used here purely operationally, and get their proper introduction in the next section.