Ansible merge list - list

I have two lists as below, List1 and List2. Field a is common in two lists. I need to merge and create a new list.
For an example, I have created 3 fields(like a,b,c), it may have 8 -10 fields.
List1:
[{"a" : "1",
"b": "2",
"c" : "3"
},
{"a" : "5",
"b": "6",
"c" : "7"
},
{"a" : "8",
"b": "9",
"c" : "10"
},
{"a" : "11",
"b": "12",
"c" : "13"
},
{"a" : "20",
"b": "212",
"c" : "213"
}
]
List2:
[{"a" : "1",
"d": "22",
"e" : "32"
},
{"a" : "5",
"d": "62",
"e" : "72"
},
{"a" : "8",
"d": "92",
"e" : "210"
},
{"a" : "11",
"d": "1222",
"e" : "1322"
}
]
New list:
[{"a" : "1",
"b": "2",
"c" : "3",
"d": "22",
"e" : "32"
},
{"a" : "5",
"b": "6",
"c" : "7",
"d": "62",
"e" : "72"
},
{"a" : "8",
"b": "9",
"c" : "10",
"d": "92",
"e" : "210"
},
{"a" : "11",
"b": "12",
"c" : "13",
"d": "1222",
"e" : "1322"
}
]

Update.
Recent filter combine can be applied to the lists of dictionaries. Given the lists
list1:
- {a: '1', b: '2', c: '3'}
- {a: '5', b: '6', c: '7'}
- {a: '8', b: '9', c: '10'}
- {a: '11', b: '12', c: '13'}
- {a: '20', b: '212', c: '213'}
list2:
- {a: '1', d: '22', e: '32'}
- {a: '5', d: '62', e: '72'}
- {a: '8', d: '92', e: '210'}
- {a: '11', d: '1222', e: '1322'}
Declare the list3
list3: "{{ list1|zip(list2)|map('combine')|list }}"
gives the expected result
list3:
- {a: '1', b: '2', c: '3', d: '22', e: '32'}
- {a: '5', b: '6', c: '7', d: '62', e: '72'}
- {a: '8', b: '9', c: '10', d: '92', e: '210'}
- {a: '11', b: '12', c: '13', d: '1222', e: '1322'}
Example of a complete playbook for testing
- hosts: localhost
vars:
list1:
- {a: '1', b: '2', c: '3'}
- {a: '5', b: '6', c: '7'}
- {a: '8', b: '9', c: '10'}
- {a: '11', b: '12', c: '13'}
- {a: '20', b: '212', c: '213'}
list2:
- {a: '1', d: '22', e: '32'}
- {a: '5', d: '62', e: '72'}
- {a: '8', d: '92', e: '210'}
- {a: '11', d: '1222', e: '1322'}
list3: "{{ list1|zip(list2)|map('combine')|list }}"
tasks:
- debug:
var: list1|to_yaml
- debug:
var: list2|to_yaml
- debug:
var: list3|to_yaml
Original.
The task below does the job
- set_fact:
list3: "{{ list3|default([]) + [item.0|combine(item.1)] }}"
loop: "{{ list1|zip(list2)|list }}"
- debug:
var: list3
gives
list3:
- a: '1'
b: '2'
c: '3'
d: '22'
e: '32'
- a: '5'
b: '6'
c: '7'
d: '62'
e: '72'
- a: '8'
b: '9'
c: '10'
d: '92'
e: '210'
- a: '11'
b: '12'
c: '13'
d: '1222'
e: '1322'
Optionally sort the lists
loop: "{{ list1|sort(attribute='a')|
zip(list2|sort(attribute='a'))|list }}"
and test the attribute 'a'
- fail:
msg: "[ERROR] Items a do no match."
loop: "{{ list1|sort(attribute='a')|
zip(list2|sort(attribute='a'))|list }}"
when: item.0.a != item.1.a

Related

Merging a list of dictionaries

I have a list of dicts as below.
list = [ {id: 1, s_id:2, class: 'a', teacher: 'b'} ]
list1 = [ {id: 1, c_id:1, rank:2, area: 34}, {id:1, c_id:2, rank:1, area: 21} ]
I want to merge the two lists on the common key-value pairs (in this case 'id:1')
Merged_list = [ {id:1, s_id:2, class: 'a', teacher: 'b', list1: {c_id:1, rank: 2, area: 34}, {c_id:2, rank: 1, area: 21} ]
How do I go about this?
Thanks
You can use
merged_list = [{**d1, **d2} for d1, d2 in zip(list1, list2)]
>>> merged_list
[{'id': 1, 's_id': 2, 'class': 'a', 'teacher': 'b', 'rank': 2, 'area': 34},
{'id': 2, 's_id': 3, 'class': 'c', 'teacher': 'd', 'rank': 1, 'area': 21}]
where {**d1, **d2} is just a neat way to combine 2 dictionaries. Keep in mind this will replace the duplicate keys of the first dictionary. If you're on Python 3.9, you could use d1 | d2.
EDIT: For the edit in your question, you can try this horrible one liner (keep in mind this will create the pair list1: [] if no matching indeces were found on list1):
list_ = [{"id": 1, "s_id": 2, "class": 'a', "teacher": 'b'}]
list1 = [{"id": 1, "c_id": 1, "rank": 2, "area": 34}, {"id": 1, "c_id": 2, "rank": 1, "area": 21}]
merged_list = [{**d, **{"list1": [{k: v for k, v in d1.items() if k != "id"} for d1 in list1 if d1["id"] == d["id"]]}} for d in list_]
>>> merged_list
[{'id': 1,
's_id': 2,
'class': 'a',
'teacher': 'b',
'list1': [{'c_id': 1, 'rank': 2, 'area': 34},
{'c_id': 2, 'rank': 1, 'area': 21}]}]
This is equivalent to (with some added benefits):
merged_list = []
for d in list_:
matched_ids = []
for d1 in list1:
if d["id"] == d1["id"]:
d1.pop("id") # remove id from dictionary before appending
matched_ids.append(d1)
if matched_ids: # added benefit of not showing the 'list1' key if matched_ids is empty
found = {"list1": matched_ids}
else:
found = {}
merged_list.append({**d, **found})
Try this
And don't forget to put " " when declare string
list = [ {"id": 1, "s_id": 2 ," class": 'a', "teacher": 'b'}, {"id": 2, "s_id" : 3, "class" : 'c', "teacher": 'd'} ]
list1 = [ {"id": 1, "rank" :2, "area" : 34}, {"id" :2, "rank" :1, "area": 21} ]
list2 = list1 + list
print(list2)

ansible nested loop over a single dict

I've got a list of dict like:
- { "a": "zzz", "b": [1, 2] }
- { "a": "yyy", "b": [7, 9] }
I need with ansible to loop over it so item would be successively:
- { "a": "zzz", "b": 1 }
- { "a": "zzz", "b": 2 }
- { "a": "yyy", "b": 7 }
- { "a": "yyy", "b": 9 }
How could I do that?
Perhaps it can be done in 1 task, but here is how i would do it:
You need 1 loop for the "master" list and then 1 dynamic loop for the list of the 'b' key. We will use an include_tasks task in order to process each dict of the "master" list, then in the included tasks file just a set_fact to loop the "b" key, and populate our variable.
CODE:
main.yml:
---
- hosts: localhost
gather_facts: false
vars:
source_var:
- { "a": "zzz", "b": [1, 2] }
- { "a": "yyy", "b": [7, 9] }
tasks:
- name: print var
include_tasks: "set_fact.yml"
with_items: "{{ source_var }}"
loop_control:
loop_var: itemoflist
- name: print var
debug:
var: target_var
included PB, called set_fact.yml:
- name: populate_var
set_fact:
target_var: "{{ target_var | default([]) + [{ 'a': itemoflist.a, 'b': item }] }}"
with_items:
- "{{ itemoflist['b'] }}"
variable created:
TASK [print var] *******************************************************************************************************************************************************************************************************
ok: [localhost] => {
"target_var": [
{
"a": "zzz",
"b": 1
},
{
"a": "zzz",
"b": 2
},
{
"a": "yyy",
"b": 7
},
{
"a": "yyy",
"b": 9
}
]
}
hope it helps.

How to modify an existing dictionary in ansible?

I'm trying to add an existing list to an existing dict in Ansible.
I have a dict "jbossvars" containing the following (ansible debug)
"jbossvars": {
"environments": {
"TEST_ENV": {
"key1": "value1",
"key2": "value2"
},
"TEST_ENV2": {
"key1": "value1",
"key2": "value2"
}
}
}
and a list "env_homes" containing the following (ansible debug)
"env_homes": [
"/opt/redhat/jboss-7.2.0/TEST_ENV",
"/opt/redhat/jboss-7.2.0/TEST_ENV2"
]
which I want to combine to a new dictionary "new_dict"
"jbossvars": {
"environments": {
"TEST_ENV": {
"key1": "value1",
"key2": "value2",
"key3": "/opt/redhat/jboss-7.2.0/TEST_ENV"
},
"TEST_ENV2": {
"key1": "value1",
"key2": "value2",
"key3": "/opt/redhat/jboss-7.2.0/TEST_ENV2"
}
}
}
The following play does not give me the desired situation:
- name: Create dict to append
set_fact:
env_homes: "{{ {'TEST_ENV': [ jbossvars.environments.TEST_ENV ] + env_homes} }}"
- name: Insert created dict into existing dict and save it into a new variable newdict
set_fact:
newdict: "{{ jbossvars.environments|combine(env_homes) }}"
- debug: var: newdict
To get the result
TEST_ENV: { "a": 1, "b": [ 2, "x1", "x2" ] }
The play below
vars:
TEST_ENV:
a: 1
b: 2
add_this:
c: [ x1, x2 ]
tasks:
- set_fact:
add_this: "{{ {'b': [ TEST_ENV.b ] + add_this.c} }}"
- set_fact:
TEST_ENV: "{{ TEST_ENV|combine(add_this) }}"
- debug:
var: TEST_ENV
gives
"TEST_ENV": {
"a": 1,
"b": [
2,
"x1",
"x2"
]
}

How compare 12 List's

so, i have 12 list's with Number , and i will compare it, with a Input with Number .I will that each List to compare with the input , and print the number similar with the input .
example:
input : 2 14 34 12 23 45
first list : ["2", "14", "18", "28","40", "48"]
output 2 14
my code:
w = raw_input("give number: ").split()
a1 = ["2", "14", "18", "28","40", "48"]
a2 = ["5", "9", "17", "21", "32", "49"]
a3 = ["4", "18", "19", "30", "47", "49"]
a4 = ["9", "15", "25", "28", "39", "43"]
a5 = ["8", "11", "13", "25", "39", "48"]
a6 = ["3", "12", "13", "14", "31", "33"]
a7 = ["3", "12", "14", "23", "26", "45"]
a8 = ["1", "10", "12", "15", "18", "37"]
a9 = ["6", "7", "17", "38", "41", "44"]
a10 = ["1", "7", "14", "17", "27", "35"]
a11 = ["15", "23", "25", "26", "39", "48"]
a12 = ["5", "12", "14", "30", "41", "48"]
for a,b,c,d,e,f,g,h,i,j,k,l in zip(a1, b2, c3, d4, e5, f6, g7, h8, i9, j10, k11, l12):
if a in w :
print "(1)", a
elif b in w:
print "(2)", b
elif c in w:
print "(3)", c
elif d in w:
print "(4)", d
elif e in w:
print "(5)", e
elif f in w:
print "(6)", f
elif g in w:
print "(7)", g
elif h in w:
print "(8)", h
elif i in w:
print "(9)", i
elif j in w:
print "(10)", j
elif k in w:
print "(11)", k
else:
print "(12)", a
This is what i come....
give number: 2 14 18 28
(1) 2
(1) 14
(1) 18
(1) 28
(8) 40
(12) 48
Can you Please help me.... Thanks!
People help me for this question(not here...)...very thanks them!!, The solution for my problem ... i write the solution maybe another people to need it...
bet_numbers = [
{"2", "14", "18", "28","40", "48"},
{"5", "9", "17", "21", "32", "49"},
{"4", "18", "19", "30", "47", "49"},
{"9", "15", "25", "28", "39", "43"},
{"8", "11", "13", "25", "39", "48"}, #created set Lists with curly braces {}
{"3", "12", "13", "14", "31", "33"},
{"3", "12", "14", "23", "26", "46"},
{"1", "10", "12", "15", "18", "37"},
{"6", "7", "17", "38", "41", "44"},
{"1", "7", "14", "17", "27", "35"},
{"15", "23", "25", "26", "39", "48"},
{"5", "12", "14", "30", "41", "48"},
]
drawn_numbers = set(raw_input("drawn numbers: ").split()) # build a set List
for index, numbers in enumerate(bet_numbers, start=1): #with enumerate(),enumerate each List
correct = drawn_numbers & numbers #with "identifier" add Input + numbers = (1), (2),....
if correct: #if statement without comparison because True is...
print "({}) {}".format(index, ', '.join(sorted(correct)))
#"({}) {}".format() = concatenate elements together .
# (index, ', '.join(sorted(correct))) = (1), (2),...sorted(correct),sorted the set lists output

Python how to create new lists from a list containing multiple lists depending on indexes

I have a list that stores lists with 5 elements. I want to create 5 new lists that store elements of each indexes. I have the following code but it seems not smart way.
>>> stats
[['1', '0', '36', '36', '3'], ['10', '0', '41', '77', '5'], ['1', '0', '631', '631', '63'], ['1', '0', '98', '98', '9'], ['9', '0', '52', '81', '6'], ['2', '0', '111', '167', '13'], ['1', '0', '98', '98', '9'], ['1', '0', '92', '92', '9'], ['2', '0', '241', '287', '26'], ['1', '0', '210', '210', '21'], ['2', '0', '336', '358', '34'], ['2', '0', '49', '57', '5'], ['5', '0', '52', '148', '7'], ['2', '0', '46', '76', '6'], ['3', '0', '33', '50', '4'], ['7', '0', '47', '70', '6'], ['1', '0', '94', '94', '9'], ['1', '0', '65', '65', '6'], ['1', '0', '66', '66', '6'], ['1', '0', '429', '429', '42'], ['1', '0', '337', '337', '33'], ['12', '0', '49', '126', '6'], ['1', '0', '47', '47', '4'], ['1', '0', '63', '63', '6'], ['1', '0', '79', '79', '7'], ['2', '0', '96', '100', '9'], ['1', '0', '36', '36', '3'], ['1', '0', '69', '69', '6'], ['6', '0', '44', '67', '5'], ['3', '0', '269', '385', '31'], ['2', '0', '78', '115', '9'], ['2', '0', '49', '52', '5'], ['3', '0', '26', '134', '9'], ['2', '0', '255', '561', '40'], ['1', '0', '75', '75', '7'], ['1', '0', '59', '59', '5'], ['2', '0', '59', '64', '6'], ['1', '0', '86', '86', '8'], ['1', '0', '63', '63', '6'], ['2', '0', '79', '100', '8'], ['4', '0', '825', '888', '86'], ['1', '0', '82', '82', '8'], ['3', '0', '65', '94', '7'], ['1', '0', '88', '88', '8'], ['1', '0', '344', '344', '34'], ['1', '0', '286', '286', '28'], ['1', '0', '73', '73', '7'], ['3', '0', '42', '69', '5'], ['1', '0', '151', '151', '15'], ['1', '0', '286', '286', '28'], ['2', '0', '47', '59', '5'], ['9', '0', '15', '41', '2'], ['2', '0', '343', '355', '34'], ['1', '0', '305', '305', '30'], ['1', '0', '238', '238', '23'], ['2', '0', '974', '2101', '153'], ['2', '0', '138', '142', '14'], ['7', '0', '45', '70', '5'], ['1', '0', '39', '39', '3']]
>>>
>>> num_requests,num_failures,min_response_time,max_response_time,avg_response_time = [], [], [], [], []
>>>
>>> for l in stats:
... num_requests.append(l[0])
... num_failures.append(l[1])
... min_response_time.append(l[2])
... max_response_time.append(l[3])
... avg_response_time.append(l[4])
...
>>> num_requests
['1', '10', '1', '1', '9', '2', '1', '1', '2', '1', '2', '2', '5', '2', '3', '7', '1', '1', '1', '1', '1', '12', '1', '1', '1', '2', '1', '1', '6', '3', '2', '2', '3', '2', '1', '1', '2', '1', '1', '2', '4', '1', '3', '1', '1', '1', '1', '3', '1', '1', '2', '9', '2', '1', '1', '2', '2', '7', '1']
It could be stored in one list which stores 5 sublist.
Solution
Just use zip with *:
(num_requests, num_failures, min_response_time, max_response_time,
avg_response_time) = zip(*stats)
This gives you tuples. Convert to lists if you need lists:
(num_requests, num_failures, min_response_time, max_response_time,
avg_response_time) = (list(x) for x in zip(*stats))
Details
A shorter example:
>>> data = [[1, 2, 3], [10, 20, 30], [100, 200, 300]]
>>> a, b, c = zip(*data)
>>> a
(1, 10, 100)
>>> b
(2, 20, 200)
>>> c
(3, 30, 300)
This is equivalent to:
a, b, c = zip(data[0], data[1], data[2])
but works for any number of sublists.
The left side uses tuple unpacking. For example, this:
x, y, z = (10, 20, 30)
assigns 10 to x, 20 to y, and 30 to z.
Performance
Measure how fast it is.
Version with append:
%%timeit
num_requests,num_failures,min_response_time,max_response_time,avg_response_time = [], [], [], [], []
for l in stats:
num_requests.append(l[0])
num_failures.append(l[1])
min_response_time.append(l[2])
max_response_time.append(l[3])
avg_response_time.append(l[4])
10000 loops, best of 3: 51 µs per loop
Version with zip:
%%timeit
(num_requests, num_failures, min_response_time, max_response_time,
avg_response_time) = zip(*stats)
100000 loops, best of 3: 8.58 µs per loop
It is about five times faster.
It takes a bit longer when you convert the tuples to lists:
%%timeit
(num_requests, num_failures, min_response_time, max_response_time,
avg_response_time) = (list(x) for x in zip(*stats))
100000 loops, best of 3: 13.3 µs per loop
Still, about four times faster.