Skip to content

Instantly share code, notes, and snippets.

@Jeff-Russ
Last active February 10, 2017 21:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jeff-Russ/22c6c98bdb3e2d963496a8e8a76787b0 to your computer and use it in GitHub Desktop.
Save Jeff-Russ/22c6c98bdb3e2d963496a8e8a76787b0 to your computer and use it in GitHub Desktop.
PHP: array_insert() to insert at index, non-destructively
<?php
// this is the most performant way to insert a integer index non-destructively
function insert_at_index(&$arr, $idx, $val)
{
$i=$idx; $copy=$arr; $arr=array(); // backup and delete array
do { $arr[$i] = $copy[$n=$i++]; unset($copy[$n]); }
while (isset($copy[$i]));
$arr += $copy; $arr[$idx] = $val;
}
<?php
/**
* Insert a value at a specified integer index non-destructively with minimal
* impact on indexing. Indexes below the insert location are not changed and
* indexes above the insertion are shift up until the point where a free slot is
* found, above which indexing is unaltered.
*
* As for sorting, integer indexes will sorted up to this free slot but not above.
* Every element above this location, whether the key is an integer or string,
* is appended to the end of the array unsorted. If the 4th argument is true the
* entire array can is sorted, with indexed element first and associative keys
* last, both in alpha/numeric order.
*
* The 5th argument can be set to true which will make the array be passed by
* reference.
*/
function array_insert(&$s_arr, $idx, $val, $full_sort=false, $by_ref=false) {
$copy = $s_arr;
if ($by_ref) $out_arr =& $s_arr;
$out_arr = array();
for ($i=(int)min(array_keys($copy)); true; $i++) {
if (isset($copy[$i])) {
if ($i<=$idx) {
if ($i<$idx) $out_arr[$i] = $copy[$i];
else { $out_arr[$i]=$val; $out_arr[$i+1]=$copy[$i]; }
unset($copy[$i]);
} else {
$above = $i+1;
$out_arr[$above] = $copy[$i];
unset($copy[$i]);
if (!isset($copy[$above])) break;
}
}
}
if ($full_sort) {
ksort($copy);
foreach ($copy as $k=>$v) {
if (is_integer($k)) { $out_arr[$k] = $copy[$k]; unset($copy[$k]); }
}
}
$out_arr += $copy;
return $out_arr;
}
<?php
trait TSequence
{
protected $seq = array();
function add($val, $idx=null)
{
if ($idx===null) {
$type = gettype($val);
$a_ob = $type==='object' && (
$val instanceof ArrayObject ||
$val instanceof ArrayAccess && $val instanceof IteratorAggregate
);
if ($type!=='array' && !$a_ob) $this->seq[] = $val;
else {
foreach ($val as $k => $v) {
if (!is_integer($k) || !isset($this->seq[$k]))
$this->seq[$k] = $v;
else $this->add($v, $k);
}
}
return $this;
}
// we can overwrite string keys or write to un-used int keys:
if (!is_integer($idx) || !isset($this->seq[$idx])) {
$this->seq[$idx] = $val; return $idx;
}
$idx++; // we'll have to put $val right after the $idx requested
// first we'll try to just assign it, if it's available:
if (!isset($this->seq[$idx])) { $this->seq[$idx] = $val; return $idx; }
// It wasn't available to our last resort is to free it up by moving some:
$copy = $this->seq; $this->seq = array();
for ($i=(int)min(array_keys($copy)); true; $i++) {
if (isset($copy[$i])) {
if ($i<=$idx) {
if ($i<$idx) $this->seq[$i] = $copy[$i];
else { $this->seq[$i]=$val; $this->seq[$i+1]=$copy[$i]; }
unset($copy[$i]);
} else {
$above = $i+1;
$this->seq[$above] = $copy[$i];
unset($copy[$i]);
if (!isset($copy[$above])) break;
}
}
}
$this->seq += $copy;
return $idx;
}
//////// Array Access Methods //////////////////////////////////////////////
function offsetSet($idx, $val) { $this->add($val, $idx); }
function offsetGet($idx) {
return isset($this->seq[$idx]) ? $this->seq[$idx] : null;
}
function offsetExists($idx) { return array_key_exists($idx, $this->seq); }
function offsetUnset($idx) { unset($this->seq[$idx]); }
//////// Other Array Related Methods ///////////////////////////////////////
function unserialize($serialized) { $this->seq = unserialize($serialized); }
function serialize() { return serialize($this->seq); }
function getIterator() { return new ArrayIterator($this->seq); }
function count() { return count( $this->seq ); }
function length() { return count( $this->seq ); }
function __toString() { return var_export($this->seq, true); }
function getArrayCopy(){ return $this->seq; }
}
class Seq implements Serializable, IteratorAggregate, ArrayAccess, Countable
{
use TSequence;
function __construct($arr) {
if (is_array($arr)) $this->seq = $arr;
elseif (method_exists($arr, 'getArrayCopy'))
$this->seq = $arr->getArrayCopy();
else $this->seq = func_get_args();
}
}
@Jeff-Russ
Copy link
Author

Jeff-Russ commented Feb 9, 2017

array_insert.php example

$original = array(
    0=>'a',
    1=>'b',
    2=>'c',
    'a string'=>'string',
    5=>'d',
    10=>'10',
    7=>'e',
    'z string'=>'another string',
);
$new = array_insert( $original, 1, 'val', true); // insert 'val' at [1] 
var_export($new);
array_insert( $original, 1, 'val', true, true); // by reference

output:

array (
  0 => 'a',
  1 => 'val',
  2 => 'b',
  3 => 'c',
  5 => 'd',
  7 => 'e',
  10 => '10',
  'a string' => 'string',
  'z string' => 'another string',
)
array (
  0 => 'a',
  1 => 'val',
  2 => 'b',
  3 => 'c',
  5 => 'd',
  7 => 'e',
  10 => '10',
  'a string' => 'string',
  'z string' => 'another string',
)

Sequence.php example

$seq = new Seq([
    0=>'a',
    1=>'b',
    2=>'c',
    'a string'=>'string',
    5=>'d',
    10=>'10',
    7=>'e',
    'z string'=>'another string',
]);
$seq[1] = 'val'; // insert 'val' at [1] 
$seq[8] = 'eig'; // insert 'val' at [8] 
echo "$seq";

output:

array (
  0 => 'a',
  1 => 'b',
  2 => 'val',
  3 => 'c',
  6 => 'd',
  'a string' => 'string',
  10 => '10',
  7 => 'e',
  'z string' => 'another string',
  8 => 'eig',
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment