i have this list in Ansible w dummy folder names:
- /tmp/another/six
- /tmp/another/five
- /hi/ho/one/abc
- /hi/ho/two/abc
- /something
i want to create a new list, based on the lowest common directory name.
e.g. for
- /tmp/another/six
- /tmp/another/five
the common name is /tmp/another
.
e.g. for
- /hi/ho/one/abc
- /hi/ho/two/abc
it is: /hi/ho
.
example output of the first example list:
- /tmp/another
- /hi/ho
- /something
i tried something along the lines of:
the_list | map('dirname') | unique | min
.
but ofc, didn't provide me w the expected results.
With the help of 'a friend', I've written this Ansible plugin:
from ansible.errors import AnsibleFilterError
import os
def lowest_common_directories(paths):
if not isinstance(paths, list):
raise AnsibleFilterError('Input should be a list of paths')
paths = [os.path.normpath(path) for path in paths]
common_dirs = {}
for i, path in enumerate(paths):
has_common = False
for j in range(len(paths)):
if i != j:
common_path = os.path.commonpath([path, paths[j]])
if common_path != '/':
has_common = True
if common_path in common_dirs:
common_dirs[common_path].add(path)
common_dirs[common_path].add(paths[j])
else:
common_dirs[common_path] = {path, paths[j]}
if not has_common:
common_dirs[path] = {path}
final_common_dirs = set()
for common in sorted(common_dirs, key=lambda x: len(x)):
if all(not c.startswith(common + '/') for c in final_common_dirs):
final_common_dirs.add(common)
return list(final_common_dirs)
class FilterModule(object):
""" Ansible custom filter plugin """
def filters(self):
return {
'lowest_common_dirs': lowest_common_directories
}
Call with:
- debug:
msg: "{{ the_paths | lowest_common_dirs }}"
Provides me w the desired result.