Created
May 11, 2012 15:56
-
-
Save cataphract/2660601 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
function num_val(&$n, $keyMode) | |
{ | |
$ncopy = $n; | |
if (!is_scalar($n)) { | |
$ncopy = gettype($ncopy); | |
/* (string)array() throws a notice on 5.4 */ | |
} elseif ((string)$n === (string)(int)$n) { | |
$n = (int)$n; | |
return true; | |
} elseif ($keyMode && is_string($n)) { | |
return true; | |
} | |
trigger_error("Not a valid " | |
. ($keyMode ? 'array key' : 'integer') . ": '$ncopy'", E_USER_WARNING); | |
return false; | |
} | |
function conv_part($arr, $n, $keyMode) | |
{ | |
if (!is_array($arr)) { | |
trigger_error("The depth of the part specification is too large", | |
E_USER_WARNING); | |
return false; | |
} | |
if (empty($arr)) { | |
trigger_error("Tried to access part of empty array", E_USER_WARNING); | |
return false; | |
} | |
if (!$keyMode) { | |
$count = count($arr); | |
if ($n >= 0) { | |
if ($n >= $count) { | |
trigger_error("Index '$n' is too large for the input", E_USER_WARNING); | |
return false; | |
} | |
return $n; | |
} else { | |
if (-($n + 1) >= count($arr)) { | |
trigger_error("Index '$n' is too large in absolute value " | |
. "for the input", E_USER_WARNING); | |
return false; | |
} | |
return count($arr) + $n; | |
} | |
} else { | |
if ($n === false) { | |
return 0; | |
} | |
if ($n === true) { | |
return count($arr) - 1; | |
} | |
$keys = array_keys($arr); | |
$search = array_search($n, $keys, true); | |
if ($search === false) { | |
trigger_error("Index '$n' not found in input", E_USER_WARNING); | |
} | |
return $search; | |
} | |
} | |
function do_unit($read, &$write, &$newReadTargets, &$newWriteTargets, | |
$index, $nextType) | |
{ | |
$value = array_values($read)[$index]; | |
if ($nextType === 'none') { | |
$write[] = $value; | |
} elseif ($nextType === 'multiple') { | |
$newSubArray = array(); | |
$write[] =& $newSubArray; | |
$newReadTargets[] = $value; | |
$newWriteTargets[] =& $newSubArray; | |
} else { //single | |
$newReadTargets[] = $value; | |
$newWriteTargets[] =& $write; | |
} | |
} | |
function span_is_all($start, $end, $step, $keyMode) | |
{ | |
return ($step === 1 && | |
(($keyMode && $start === false && $end === true) | |
|| | |
(!$keyMode && $start === 0 && $end === -1))) | |
|| | |
($step === -1 && | |
(($keyMode && $start === true && $end === false) | |
|| | |
(!$keyMode && $start === -1 && $end === 0))); | |
} | |
function do_work_span(&$readTargets, &$writeTargets, | |
$start, $end, $step, $type, $keyMode) | |
{ | |
$newReadTargets = array(); | |
$newWriteTargets = array(); | |
if (($count = count($readTargets)) !== count($writeTargets)) { | |
trigger_error("Logic error in the algorithm", E_USER_ERROR); | |
} | |
for ($i = 0; $i < $count; $i++) { | |
$r = $readTargets[$i]; | |
$w =& $writeTargets[$i]; | |
/* As an exception, if we request all elements allow empty arrays */ | |
if ($r === array() && span_is_all($start, $end, $step, $keyMode)) { | |
continue; | |
} | |
$s = conv_part($r, $start, $keyMode); | |
$e = conv_part($r, $end, $keyMode); /* inclusive */ | |
if ($s === false || $e === false) | |
return false; | |
for ($j = $s; | |
$step > 0 && $j <= $e || $step < 0 && $j >= $e; | |
$j += $step) | |
{ | |
do_unit($r, $w, $newReadTargets, $newWriteTargets, $j, $type); | |
} | |
} | |
$readTargets = $newReadTargets; | |
$writeTargets = $newWriteTargets; | |
return true; | |
} | |
function do_work_indexes(&$readTargets, &$writeTargets, | |
$indexes, $type, $keyMode) | |
{ | |
$newReadTargets = array(); | |
$newWriteTargets = array(); | |
if (($count = count($readTargets)) !== count($writeTargets)) { | |
trigger_error("Logic error in the algorithm", E_USER_ERROR); | |
} | |
for ($i = 0; $i < $count; $i++) { | |
$r = $readTargets[$i]; | |
$w =& $writeTargets[$i]; | |
foreach ($indexes as $ind) { | |
$j = conv_part($r, $ind, $keyMode); | |
if ($j === false) | |
return false; | |
do_unit($r, $w, $newReadTargets, $newWriteTargets, $j, $type); | |
} | |
} | |
$readTargets = $newReadTargets; | |
$writeTargets = $newWriteTargets; | |
return true; | |
} | |
function do_work_single_index(&$readTargets, &$writeTargets, | |
$index, $type, $keyMode) | |
{ | |
$newReadTargets = array(); | |
$newWriteTargets = array(); | |
if (($count = count($readTargets)) !== count($writeTargets)) { | |
trigger_error("Logic error in the algorithm", E_USER_ERROR); | |
} | |
for ($i = 0; $i < $count; $i++) { | |
$r = $readTargets[$i]; | |
$w =& $writeTargets[$i]; | |
$j = conv_part($r, $index, $keyMode); | |
if ($j === false) { | |
return false; | |
} | |
// var_dump('pre_do_unit', $r, $w); | |
do_unit($r, $w, $newReadTargets, $newWriteTargets, $j, $type); | |
} | |
$readTargets = $newReadTargets; | |
$writeTargets = $newWriteTargets; | |
return true; | |
} | |
function array_part($arr, array $definition, $keyMode = false) | |
{ | |
$result = array(); | |
$readTargets = array($arr); | |
$firstPart = reset($definition); | |
if (is_array($firstPart)) { | |
$result[] = array(); | |
$writeTargets = array(&$result[0]); | |
} else { | |
$writeTargets = array(&$result); | |
} | |
for ($depth = 0; | |
($partSpec = current($definition)) !== false; | |
$depth++) { | |
$nextPartSpec = next($definition); | |
if ($nextPartSpec === false) | |
$nextPartSpec = 'none'; | |
elseif (is_array($nextPartSpec)) | |
$nextPartSpec = 'multiple'; | |
else | |
$nextPartSpec = 'single'; | |
// var_dump($readTargets, $writeTargets, $nextPartSpec); | |
if (is_array($partSpec)) { | |
if (key_exists('start', $partSpec) || | |
key_exists('end', $partSpec)) { | |
$start = $keyMode ? false /* special value */ : 0; | |
$end = $keyMode ? true /* special value */ : -1; | |
$step = 1; | |
foreach ($partSpec as $k => $v) { | |
if ($v !== null && !num_val($v, $keyMode)) { | |
return false; | |
} | |
if ($k === 'start') { | |
if ($v === null) { | |
continue; /* allow null default to 1st element */ | |
} | |
$start = $v; | |
} elseif ($k === 'end') { | |
if ($v === null) { | |
continue; /* allow null default to last element */ | |
} | |
$end = $v; | |
} elseif ($k === 'step') { | |
if (!num_val($v, false)) { | |
return false; | |
} | |
$step = $v; | |
if ($step === 0) { | |
trigger_error("Step cannot be 0", E_USER_WARNING); | |
return false; | |
} | |
} else { | |
trigger_error("Span definitions should only include " | |
. "elements with keys 'start', 'end' and 'step', " | |
. "given '$k", E_USER_WARNING); | |
return false; | |
} | |
} | |
if ($step < 0) { | |
/* revert defaults for start and end | |
* if we're moving backwards */ | |
if ($start === false) { | |
$start = true; | |
} | |
if ($end === true) { | |
$end = false; | |
} | |
} | |
if (!do_work_span($readTargets, $writeTargets, | |
$start, $end, $step, $nextPartSpec, $keyMode)) { | |
return false; | |
} | |
} else { | |
$previousKey = -1; | |
foreach ($partSpec as $k => $v) { | |
if ($previousKey + 1 !== $k) { | |
trigger_error("Part definition at depth " | |
. ($depth + 1) . " is not a sequential array"); | |
} | |
if (!num_val($v, $keyMode)) { | |
return false; | |
} | |
} | |
if (!do_work_indexes($readTargets, $writeTargets, | |
$partSpec, $nextPartSpec, $keyMode)) { | |
return false; | |
} | |
} | |
} else { | |
if (!num_val($partSpec, $keyMode)) { | |
return false; | |
} | |
if (!do_work_single_index($readTargets, $writeTargets, | |
$partSpec, $nextPartSpec, $keyMode)) | |
return false; | |
} | |
} | |
return $result[0]; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
include "source.php"; | |
$array = [ | |
[1, 2, 'foo' => 3], | |
['a', 'foo' => 'b', 'c', 'd'], | |
]; | |
echo 'All, {-1}', "\n"; | |
print_r( | |
array_part($array, [ | |
['start'=>0, 'end'=>-1], | |
[-1], | |
]) | |
); | |
/* | |
[ | |
[3], | |
['d'], | |
] | |
*/ | |
echo 'All, -1', "\n"; | |
print_r( | |
array_part($array, [ | |
['start'=>0, 'end'=>-1], | |
-1, | |
]) | |
); | |
/* | |
[3, 'd'] | |
*/ | |
echo "\n", '0, -1', "\n"; | |
print_r( | |
array_part($array, [ | |
0, | |
-1, | |
]) | |
); | |
/* | |
3 | |
*/ | |
echo "\n", '{0}, -1', "\n"; | |
print_r( | |
array_part($array, [ | |
[0], | |
-1, | |
]) | |
); | |
/* | |
[3] | |
*/ | |
echo "\n", 'All, -2;;', "\n"; | |
print_r( | |
array_part($array, [ | |
['start'=>0, 'end'=>-1], | |
['start'=>-2, 'end'=>-1], | |
]) | |
); | |
/* | |
[ | |
[2, 3], | |
['c', 'd'] | |
] | |
*/ | |
echo "\n", 'All, "foo" (KEY MODE)', "\n"; | |
print_r( | |
array_part($array, [ | |
['start'=>null, 'end'=>null], | |
"foo", | |
], true) | |
); | |
/* | |
[3, 'b'] | |
*/ | |
echo "\n", 'All, {"foo"->START, -1} (KEY MODE)', "\n"; | |
print_r( | |
array_part($array, [ | |
['start'=>null, 'end'=>null], | |
['start' => "foo", 'step' => -1], | |
], true) | |
); | |
/* | |
[ | |
[3, 2, 1], | |
['b', 'a'], | |
] | |
*/ | |
echo "\n", 'Empty array: All, {-1}', "\n"; | |
print_r( | |
array_part([], [ | |
['start'=>0, 'end'=>-1], | |
[-1], | |
]) | |
); | |
/* | |
[] | |
*/ | |
echo "\n", '[[1,2],[]]: {-1}, All', "\n"; | |
print_r( | |
array_part([[1,2], []], [ | |
[-1], | |
['start'=>0, 'end'=>-1], | |
]) | |
); | |
/* | |
[[]] | |
*/ | |
echo "\n", '[[[1,2]],[]]: All, All, All (KEY MODE)', "\n"; | |
print_r( | |
array_part([[[1,2]], []], [ | |
['start'=>null], | |
['start'=>null], | |
['start'=>null], | |
], true) | |
); | |
/* | |
original array | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment