You can update them dynamically by indexing each individual element :
For age :
WITH s AS
(
SELECT ('{'||idx-1||',age}')::text[] AS path
FROM tab
CROSS JOIN jsonb_array_elements(jsdata)
WITH ORDINALITY arr(j,idx)
WHERE j->>'name'='test'
)
UPDATE tab
SET jsdata = jsonb_set(jsdata,s.path,'"15"',false)
FROM s
For town :
WITH s AS
(
SELECT ('{'||idx-1||',town}')::text[] AS path
FROM tab
CROSS JOIN jsonb_array_elements(jsdata)
WITH ORDINALITY arr(j,idx)
WHERE j->>'name'='test'
)
UPDATE tab
SET jsdata = jsonb_set(jsdata,s.path,'"55"',false)
FROM s
For phone :
WITH s AS
(
SELECT ('{'||idx-1||',phone}')::text[] AS path
FROM tab
CROSS JOIN jsonb_array_elements(jsdata)
WITH ORDINALITY arr(j,idx)
WHERE j->>'name'='test'
)
UPDATE tab
SET jsdata = jsonb_set(jsdata,s.path,'"1111"',false)
FROM s
Demo
Or directly at a time :
WITH s AS
(
SELECT ('{'||idx-1||',phone}')::text[] AS path_phone,
('{'||idx-1||',town}')::text[] AS path_town,
('{'||idx-1||',age}')::text[] AS path_age
FROM tab
CROSS JOIN jsonb_array_elements(jsdata)
WITH ORDINALITY arr(j,idx)
WHERE j->>'name'='test'
)
UPDATE tab
SET jsdata = jsonb_set(jsonb_set(jsonb_set(jsdata,
s.path_phone,
'"1111"',
false),
path_town,
'"55"',
false),
s.path_age,
'"20"',
false)
FROM s
Demo