First of all, you cannot do it in verilog the way you proposed. You need to have the loop setting those bits separately. The only slight problem in your case is to figure out correct offsetts and widths.
BTW, your decalaration of data logic [NUM-1:0] [31:0] data; does not correspond the proposed usage int the for loop. You need to swap the ranges.
Also, size of the list is smaller than you need for the concat. If i am not mistaken, you should use (NUM-1)/2 to be precise. Check me :-).
I propose you use the following in order to simplify the code: use a separate temp variable for extracting elements form data and use it in the following concat. BTW, there is no need to use assign.
logic [31:0] [NUM-1:0] data; // << swap array ranges
logic [3:0] addr, cmd, testvalue;
logic [((NUM-1)/2)*32+$bits(addr)+$bits(cmd)+$bits(testvalue)-1:0] list; //<< fix the width
logic [31:0][(NUM-1)/2-1:0] list_data;
always_comb begin
for(int i = 0; i < NUM; i+=2)
list_data[i/2] = data[i];
end
always_comb
list = {addr, cmd, list_data, testvalue};
or if you wish, yhou can use assign for logic
assign list = {addr, cmd, list_data, testvalue};
Another variation is to use functions. This one should be definied inside the module in order to see data. Or you can pass data as an argument.
function logic [31:0][(NUM-1)/2-1:0] list_data();
for(int i = 0; i < NUM; i+=2)
list_data[i/2] = data[i];
endfunction
assign // or always_comb
list = {addr, cmd, list_data(), testvalue};