Asked  7 Months ago    Answers:  5   Viewed   36 times

When I use array_merge() with associative arrays I get what I want, but when I use them with numerical key arrays the keys get changed.

With + the keys are preserved but it doesn't work with associative arrays.

I don't understand how this works, can anybody explain it to me?

 Answers

51

Because both arrays are numerically-indexed, only the values in the first array will be used.

The + operator returns the right-hand array appended to the left-hand array; for keys that exist in both arrays, the elements from the left-hand array will be used, and the matching elements from the right-hand array will be ignored.

http://php.net/manual/en/language.operators.array.php

array_merge() has slightly different behavior:

If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, however, the arrays contain numeric keys, the later value will not overwrite the original value, but will be appended. Values in the input array with numeric keys will be renumbered with incrementing keys starting from zero in the result array.

http://php.net/manual/en/function.array-merge.php

Wednesday, March 31, 2021
 
ALH
answered 7 Months ago
ALH
17

You can use array_replace and array_filter

$mergedArray = array_replace($b, array_filter($a));

The result would be:

array(3) {
  ["key1"]=>
  string(19) "key1 from prioritar"
  ["key2"]=>
  string(24) "key2 from LESS prioritar"
  ["my_problem"]=>
  string(18) "I REACHED MY GOAL!"
}
Wednesday, March 31, 2021
 
Shreejibawa
answered 7 Months ago
35

yield

You can use a generator for an elegant solution. At each iteration, yield twice—once with the original element, and once with the element with the added suffix.

The generator will need to be exhausted; that can be done by tacking on a list call at the end.

def transform(l):
    for i, x in enumerate(l, 1):
        yield x
        yield f'{x}_{i}'  # {}_{}'.format(x, i)

You can also re-write this using the yield from syntax for generator delegation:

def transform(l):
    for i, x in enumerate(l, 1):
        yield from (x, f'{x}_{i}') # (x, {}_{}'.format(x, i))

out_l = list(transform(l))
print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']

If you're on versions older than python-3.6, replace f'{x}_{i}' with '{}_{}'.format(x, i).

Generalising
Consider a general scenario where you have N lists of the form:

l1 = [v11, v12, ...]
l2 = [v21, v22, ...]
l3 = [v31, v32, ...]
...

Which you would like to interleave. These lists are not necessarily derived from each other.

To handle interleaving operations with these N lists, you'll need to iterate over pairs:

def transformN(*args):
    for vals in zip(*args):
        yield from vals

out_l = transformN(l1, l2, l3, ...)

Sliced list.__setitem__

I'd recommend this from the perspective of performance. First allocate space for an empty list, and then assign list items to their appropriate positions using sliced list assignment. l goes into even indexes, and l' (l modified) goes into odd indexes.

out_l = [None] * (len(l) * 2)
out_l[::2] = l
out_l[1::2] = [f'{x}_{i}' for i, x in enumerate(l, 1)]  # [{}_{}'.format(x, i) ...]

print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']

This is consistently the fastest from my timings (below).

Generalising
To handle N lists, iteratively assign to slices.

list_of_lists = [l1, l2, ...]

out_l = [None] * len(list_of_lists[0]) * len(list_of_lists)
for i, l in enumerate(list_of_lists):
    out_l[i::2] = l

zip + chain.from_iterable

A functional approach, similar to @chrisz' solution. Construct pairs using zip and then flatten it using itertools.chain.

from itertools import chain
# [{}_{}'.format(x, i) ...]
out_l = list(chain.from_iterable(zip(l, [f'{x}_{i}' for i, x in enumerate(l, 1)]))) 

print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']

iterools.chain is widely regarded as the pythonic list flattening approach.

Generalising
This is the simplest solution to generalise, and I suspect the most efficient for multiple lists when N is large.

list_of_lists = [l1, l2, ...]
out_l = list(chain.from_iterable(zip(*list_of_lists)))

Performance

Let's take a look at some perf-tests for the simple case of two lists (one list with its suffix). General cases will not be tested since the results widely vary with by data.

enter image description here

Benchmarking code, for reference.

Functions

def cs1(l):
    def _cs1(l):
        for i, x in enumerate(l, 1):
            yield x
            yield f'{x}_{i}'

    return list(_cs1(l))

def cs2(l):
    out_l = [None] * (len(l) * 2)
    out_l[::2] = l
    out_l[1::2] = [f'{x}_{i}' for i, x in enumerate(l, 1)]

    return out_l

def cs3(l):
    return list(chain.from_iterable(
        zip(l, [f'{x}_{i}' for i, x in enumerate(l, 1)])))

def ajax(l):
    return [
        i for b in [[a, '{}_{}'.format(a, i)] 
        for i, a in enumerate(l, start=1)] 
        for i in b
    ]

def ajax_cs0(l):
    # suggested improvement to ajax solution
    return [j for i, a in enumerate(l, 1) for j in [a, '{}_{}'.format(a, i)]]

def chrisz(l):
    return [
        val 
        for pair in zip(l, [f'{k}_{j+1}' for j, k in enumerate(l)]) 
        for val in pair
    ]
Thursday, July 15, 2021
 
Kenny
answered 4 Months ago
85

Use the + operator.

Compare array_merge to + operator:

<?php

$a1 = array(0=>"whatever",);
$a2 = array(0=>"whatever",1=>"a",2=>"b");

print_r(array_merge($a1,$a2));
print_r($a1+$a2);
?>

Output:

Array
(
    [0] => whatever
    [1] => whatever
    [2] => a
    [3] => b
)
Array
(
    [0] => whatever
    [1] => a
    [2] => b
)

The + operator still works if your associative array has the numerical keys out-of-order:

<?php

$a1 = array(0=>"whatever",);
$a2 = array(1=>"a",0=>"whatever",2=>"b");

print_r(array_merge($a1,$a2));
print_r($a1+$a2);
?>

Output:

Array
(
    [0] => whatever
    [1] => a
    [2] => whatever
    [3] => b
)
Array
(
    [0] => whatever
    [1] => a
    [2] => b
)

Notice array_merge in this case creates a new key. Not desirable...

Thursday, July 29, 2021
 
kinske
answered 3 Months ago
37

Hey, you probably have a duplicate reference in XCode to CJSONDeserializer, so it's compiled and linked twice.

Saturday, October 16, 2021
 
demaxSH
answered 1 Week ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :