array_splice on JSON array in php

Viewed 49

target array

{"array":[{"Name": "John Doe", "mail": "john-doe@gmail.com"}, {"Name": "Alex Smith", "mail": "alex-smith@gmx.com"}]}

I now want to insert the $insert between "John Doe" and "Alex Smith"

$insert = array("Name"=>"Thomas Dover", "mail"=>"thomas-dover@icloud.com");


$counter = 0;

foreach($target as $x => $val) {

    foreach($val as $key => $v) {

     if ($counter == 0) {
      array_splice($target["array"], 1, 0, $insert);
     }


     }
$counter++;

}

echo json_encode($target);
2 Answers

You don't need to use the foreach for the insertion:

<?php
$target = json_decode(
    '{
        "array": [
            {
                "Name": "John Doe",
                "mail": "john-doe@gmail.com"
            },
            {
                "Name": "Alex Smith",
                "mail": "alex-smith@gmx.com"
    
            }
        ]
    }',
    true
);

$insert = [
    'Name' => 'Thomas Dover',
    'mail' => 'thomas-dover@icloud.com'
];

array_splice(
    $target['array'],
    1,
    0,
    [$insert]
);

echo json_encode($target, JSON_PRETTY_PRINT);

Output:

{
    "array": [
        {
            "Name": "John Doe",
            "mail": "john-doe@gmail.com"
        },
        {
            "Name": "Thomas Dover",
            "mail": "thomas-dover@icloud.com"
        },
        {
            "Name": "Alex Smith",
            "mail": "alex-smith@gmx.com"
        }
    ]
}

Added

In case you want to insert that item after "John Doe", not assuming its position:

foreach ($target['array'] as $index => $item) {
    if ($item['Name'] === 'John Doe') {
        array_splice(
            $target['array'],
            $index + 1,
            0,
            [$insert]
        );
        break;
    }
}

Or before "Alex Smith":

foreach ($target['array'] as $index => $item) {
    if ($item['Name'] === 'Alex Smith') {
        array_splice(
            $target['array'],
            $index,
            0,
            [$insert]
        );
        break;
    }
}

Or between "John Doe" and "Alex Smith":

foreach ($target['array'] as $index => $item) {
    if ($item['Name'] === 'John Doe') {
        ++$index;
        break;
    }
}
for (; isset($target['array'][$index]); ++$index) {
    $item = $target['array'][$index];
    if ($item['Name'] === 'Alex Smith') {
        array_splice(
            $target['array'],
            $index,
            0,
            [$insert]
        );
        break;
    }
}

Or if the three need to be consecutive:

foreach ($target['array'] as $index => $item) {
    if ($item['Name'] === 'John Doe') {
        if (
            isset($target['array'][$index+1])
            && $target['array'][$index+1]['Name'] === 'Alex Smith'
        ) {
            array_splice(
                $target['array'],
                $index+1,
                0,
                [$insert]
            );
        }
        break;
    }
}

If you don't like nested if:

$found = false;
foreach ($target['array'] as $index => $item) {
    if ($item['Name'] === 'John Doe') {
        $found = true;
        break;
    }
}
if (
    $found
    && isset($target['array'][$index+1])
    && $target['array'][$index+1]['Name'] === 'Alex Smith'
) {
    array_splice(
        $target['array'],
        $index+1,
        0,
        [$insert]
    );
}

There’s two ways to interpret the question. If it is “how to use array_splice” then Pedro’s answer is perfect. If it is “how to search and insert between two specific items” then the answer gets more complex.

I’ve seen versions of this code written with native functions such as array_search and array_column` however I personally find it unreadable, and I’m not certain if there’s a major performance gain.

This code is pretty heavily commented so I’ll let it speak for itself.

$json = '{"array":[{"Name": "John Doe", "mail": "john-doe@gmail.com"}, {"Name": "Alex Smith", "mail": "alex-smith@gmx.com"}]}';

$target = json_decode($json, true);

$insert = array("Name"=>"Thomas Dover", "mail"=>"thomas-dover@icloud.com");

// We will overwrite $val later so we want a reference
foreach($target as $x => &$val) {
    
    // Temporary array
    $between = [];
    
    // Flag for if/when we find the first person
    $foundStart = false;
    
    foreach($val as $key => $v) {
        
        // If we found the first person previously, and this is our second person
        if($foundStart && 'Alex Smith' === $v['Name']) {
            // Insert
            $between[] = $insert;
            
            // Reset the flag just in case the second person is found again
            $foundStart = false;
        }elseif('John Doe' === $v['Name']){
            
            // Flag that we found the first person
            $foundStart = true;
        }
        
        // No matter what, append our current item to the temp array
        $between[] = $v;
    }
    
    // Overwrite our original loop's variable with our temporary
    $val = $between;
}

// Clean up reference variable so surprises don't happen
unset($val);

echo json_encode($target);

Demo: https://3v4l.org/DnJht

Note: There’s edge cases to handle, too, specifically what if there are duplicates in the source array, or what if the insertion point cannot be determined, but I’ll leave that logic for someone else.

Edit As noted, depending on your actual source array you might be able to just skip the outer loop completely which also removes the need for a reference variable.

$json = '{"array":[{"Name": "John Doe", "mail": "john-doe@gmail.com"}, {"Name": "Alex Smith", "mail": "alex-smith@gmx.com"}]}';

$target = json_decode($json, true);

$insert = array("Name"=>"Thomas Dover", "mail"=>"thomas-dover@icloud.com");

// Temporary array
$between = [];
    
// Flag for if/when we find the first person
$foundStart = false;
    
foreach($target['array'] as $key => $v) {
        
    // If we found the first person previously, and this is our second person
    if($foundStart && 'Alex Smith' === $v['Name']) {
        // Insert
        $between[] = $insert;
            
        // Reset the flag just in case the second person is found again
        $foundStart = false;
    }elseif('John Doe' === $v['Name']){
            
        // Flag that we found the first person
        $foundStart = true;
    }
        
    // No matter what, append our current item to the temp array
    $between[] = $v;
}
    
// Overwrite our original loop's variable with our temporary
$target['array'] = $between;

echo json_encode($target);

Demo: https://3v4l.org/2KPIb

Related