3

Yes, this is a bit of a trick question; one array (without copies), as opposed to any odd array. Let me explain, so let's start here ;

$a = array ( 'one' => 1, 'two' => 2, 'three' => 3, 'four' => 4, 'five' => 5, 'six' => 6 ) ;

Pretend that this array is long, over a hundred long. I loop through it, step by step, but at some point (let's make up that this happens at the second item) something happens. Maybe the data is funky. Nevertheless, we need to add some items to it for later processing, and then keep looping through it, without losing the current position. Basically, I would like to do something like this ;

echo current ( $a ) ;  // 'two'
array_insert ( $a, 'four', 'new_item', 100 ) ;
echo current ( $a ) ;  // 'two'

Definition for array_insert is ( $array, $key_where_insert_happens, $new_key, $new_value ) ; Of course $new_key and $new_value should be wrapped in an array wrapper, but that's besides the point right now. Here's what I want to see happening after the above code having ran ;

print_r ( $a ) ; // array ( 'one' => 1, 'two' => 2, 'three' => 3, 'four' => 4, 'new_item' => 100, 'five' => 5, 'six' => 6 ) ;
echo current ( $a ) ;  // 'two'

Whenever you use array_splice, array_slice, array_push or most of the other array fiddling functions, you basically create a copy of the array, and then you can copy it back, but this breaks the reference to the original array and the position as well, and my loop above breaks. I could use direct reference (ie. $a['new_item'] = 'whatever;) or put it at the end, but none of these will insert items into a given position.

Any takers? How can I do a true insert into an associative array directly (that's being processed elsewhere)? My only solution so far is to ;

  1. record the position (current())
  2. Do the splice/insert (array_slice)
  3. overwrite old array with new ($old = $new)
  4. search the new position (first reset() then looping through to find it [!!!!!!])

Surely there's a better, simpler and more elegant way for doing something that's currently kludgy, heavy and poorly hobbled together? Why isn't there a array_set_position ( $key ) function that quickly can help this out, or an array_insert that works directly on the same array (or both)?

7
  • 1
    Can you guarantee that the item you're adding is AFTER the current position? If not, then it won't get processed anyway.
    – Ed Marty
    Commented May 9, 2011 at 2:08
  • It sounds like you would be better off using array_map
    – samshull
    Commented May 9, 2011 at 2:12
  • Ed, yes, an apt point. Mostly this is a stack I'm iterating over, and events early on the stack might add events to be run later. Commented May 9, 2011 at 2:41
  • 1
    samshull, not sure how you see a usage for that? Array_map iterates and runs functions over those items, but does not alter the number of items in an array, does it? Commented May 9, 2011 at 2:43
  • Well, I've gone through google.com/codesearch/p?hl=en#EKZaOgYQHwo/unstable/sources/…, and nothing jumps out, except some room for improvement with the Z_ARRVAL_PP implementation (near this comment in the source files; "/* * This is where the magic happens. */" :) Brilliant. Commented May 9, 2011 at 4:51

2 Answers 2

3

Maybe I'm not understanding you correctly but have you looked into array_splice()?

This answer might also interest you.


Would something like this work?

function array_insert($input, $key, $value)
{
    if (($key = array_search($key, array_keys($input))) !== false)
    {
        return array_splice($input, $key, 1, $value);
    }

    return $input;
}


This was the best I could come up with:

$a = array
(
    'one' => 1,
    'two' => 2,
    'three' => 3,
    'four' => 4,
    'five' => 5,
    'six' => 6,
);

ph()->Dump(next($a)); // 2
array_insert($a, 'four', array('new_item' => 100));
ph()->Dump(current($a)); // 2

function array_insert(&$array, $key, $data)
{
    $k = key($array);

    if (array_key_exists($key, $array) === true)
    {
        $key = array_search($key, array_keys($array)) + 1;
        $array = array_slice($array, null, $key, true) + $data + array_slice($array, $key, null, true);

        while ($k != key($array))
        {
            next($array);
        }
    }
}

ph()->Dump($a);

/*
Array
(
    [one] => 1
    [two] => 2
    [three] => 3
    [four] => 4
    [new_item] => 100
    [five] => 5
    [six] => 6
)
*/

I don't think it's possible to set an array internal pointer without looping.

8
  • Hmm, well it doesn't keep (internal) positions of arrays intact, so I still need to reset and search/set it, no? I don't have a problem with the actual splicing, it's just that all splicings on offer destroys internal counters, forcing me to deal with a rather expensive manual operation. Commented May 9, 2011 at 2:45
  • @AlexanderJohannesen: Yeah, I think I don't fully understand your question. Could you post some dummy data and the respective code / output?
    – Alix Axel
    Commented May 9, 2011 at 2:52
  • @Alix, yes, ok, I've updated the question. It's perhaps more about a better seek_and_set array position using keys question ... :) Commented May 9, 2011 at 3:10
  • @AlexanderJohannesen: Got it. I don't see any way to do that but let me think about it for a while.
    – Alix Axel
    Commented May 9, 2011 at 3:15
  • @AlexanderJohannesen: I just realized that the code I came up with was exactly the algorithm you described... Anyway, this doesn't seem to be possible, because array_set_position(): pt.php.net/manual/en/function.next.php#52362, pt.php.net/manual/en/function.next.php#83087, pt.php.net/manual/en/function.current.php#83212 and so on...
    – Alix Axel
    Commented May 9, 2011 at 3:45
2

Rather than using the associative array as the event queue, keep a separate integer-indexed array. Loop over an explicit index variable rather than using the internal array position.

$events = array_keys($a);
for ($i=0; $i < count($events); ++$i) {
    ...
    /* if there's an event to add after $j */
    array_splice($events, $j+1, 0, array($new_event_key));
    $a[$new_event_key] = $new_event_data;
    ...
}

To keep things more cohesive, you can package the two arrays into an event queue class.

3
  • Yes, that's one way, although my app rely on the keys for other things. Perhaps it needs to be split, leaving more serializing work to be done, but maybe there is no better way? Hmm. Commented May 9, 2011 at 4:02
  • @Alexander: if a built-in data structure doesn't meet your needs, your best bet is to implement your own. With an event queue class, you can implement the Iterator and ArrayAccess interfaces. It will function as an array, but will give you greater control over positioning. You can even add methods to get and set the position (possibly using overloading).
    – outis
    Commented May 10, 2011 at 8:04
  • yes, of course, and I'm starting to lean that way. First I'll see how expensive the insert/seek operation on an array is, and if proven bad I'll do exactly that. :) It just seemed like a hashmap with internal pointer would have at least a way to set the pointer by key, but I may have to go all OO on 'im to make it snappy enough. Commented May 10, 2011 at 20:55

Not the answer you're looking for? Browse other questions tagged or ask your own question.