Related
I am trying to figure out how I can start looping through an array at a different index but when it reaches the end it loops back to the beginning and finishes the array. Basically, I need to be able to dynamically change the offset of the array.
What I am trying to do it associate a letter of the alphabet with a different alphabet letter to mix things up for a string.
Let's say I have a random array like so
$arr = array('a' => 'g', 'b' => 'w', 'c' => 'j', 'd' => 'y', 'e' => 'k');
Then I have a string like so
$string = 'abcde';
And let's say I need to start at index in the array at 2 which would be 'c' => 'j' then finish the array to the end and then loop back to the beginning until it is finished.
What I want to do is replace each letter with the corresponding letter associated with it in the array. So the final string after it is replaced would look like
I would reconstruct the array with
$build = strtr($string,$arr);
which would echo gwjyk
But I need to start at a random point in the array and then finish it and go back to the beggining and finish the entire array.
So maybe I have an offset of 2.
$offset = 2;
As I mentioned in the comments, I would approach this using array_slice and then merging the two arrays in order to simply get a new array, then loop through it from start to finish.
Here's a fully functional solution (and a runnable version)- although I'd like to point out that the offset really doesn't change the results at all:
/**
* Goes through a string and replaces letters based on an array "map".
*
* #param string - $string
* #param int - $offset
*
* #return string
*/
function change_letters( $string, $offset ) {
$letters = ['a' => 'g', 'b' => 'w', 'c' => 'j', 'd' => 'y', 'e' => 'k'];
// some defensive code to prevent notices or errors
if ( (int)$offset > count($letters)) {
echo '<p>Error: Offset is larger than the array of letters!</p>';
return $string;
}
// build new array based on passed-in offset
$new_array = array_slice($letters, $offset) + array_slice($letters, 0, $offset);
// at this point, array is ['c' => 'j', 'd' => 'y', 'e' => 'k', 'a' => 'g', 'b' => 'w']
// loop through the letters to replace...
foreach($new_array AS $from => $to) {
// swaps all instances of the "from" letter to the "to" letter in the string.
// NOTE: this could be easily modified to only replace n instances of the "from" letter
// like so: $string = str_ireplace( $from, $to, $string, 1); - would only replace 1 instance
$string = str_ireplace( $from, $to, $string );
}
return $string;
}
// Sample usage:
$word = 'abcde';
$new_word = change_letters( $word, 2); // "gwjk"
var_dump(2, $new_word);
$new_word = change_letters( $word, 5); // "gwjk"
var_dump(5, $new_word);
$new_word = change_letters( $word, 6); // "abcde"
var_dump(5, $new_word);
You can try:
<?php
$arr = array(1 => 2, 3 => 4, 5 => 6, 7 => 8, 9 => 0);
$STARTING_KEY = 3;
$array_keys = array_keys($arr);
$starting_index = array_search($STARTING_KEY, $array_keys);
for ($i = $starting_index; $i < sizeof($arr); $i++) {
echo $arr[$array_keys[$i]] . "\n";
}
for ($i = 0; $i < $starting_index; $i++) {
echo $arr[$array_keys[$i]] . "\n";
}
This will test all possible offsets for the string
$arr = array('a' => 'g', 'b' => 'w', 'c' => 'j', 'd' => 'y', 'e' => 'k');
$str = "abcde";
$strlen = strlen($str);
$keys = array_keys($arr);
for ($j = 0; $j < $strlen; $j++)
{
$startIndex = $j;
echo "offset: " . $startIndex . ": ";
for ($i = $startIndex; $i < $strlen; $i++ )
{
$char = substr( $str, $i, 1 );
echo $arr[$char];
}
for ($i = 0; $i < $startIndex; $i++ )
{
$char = substr( $str, $i, 1 );
echo $arr[$char];
}
echo "\n";
}
Output:
offset: 0: gwjyk
offset: 1: wjykg
offset: 2: jykgw
offset: 3: ykgwj
offset: 4: kgwjy
As mentioned in the comment, another option for your example data could be using array_slice and setting the offset and the length parameters and use array_merge:
$arr = array('a' => 'g', 'b' => 'w', 'c' => 'j', 'd' => 'y', 'e' => 'k');
$top = array_slice($arr, 0, 2);
$rest = array_slice($arr, 2);
print_r(array_merge($rest, $top));
Array
(
[c] => j
[d] => y
[e] => k
[a] => g
[b] => w
)
All that array slicin’n’dicing or using two loops to loop from x to end first, and start up to x second, is fine … but they don’t make for the most readable code IMHO.
Such an “offsetted circling-through” can be achieved in a quite trivial way with a numerically indexed array - a simple for loop, and the index “clamped down” by using modulo with the total number of array elements.
So in a case like this, I would perhaps prefer the following approach:
$arr = array('a' => 'g', 'b' => 'w', 'c' => 'j', 'd' => 'y', 'e' => 'k');
$c = count($arr);
$search = array_keys($arr);
$replace = array_values($arr);
$offset = 2; // zero-based
for( $i = 0; $i < $c; ++$i ) {
$idx = ( $i + $offset ) % $c;
echo $search[$idx] . ' => ' . $replace[$idx] . "<br>\n";
}
// result:
// c => j
// d => y
// e => k
// a => g
// b => w
We all know that arrays are actually ordered trees in PHP. Given that, an array index (integer key) need not be in any strict order or even exist at all. So, given an array like:
array( 1 => 'A', 5 => 'B', 2 => 'C', 3 => 'D', 6 => 'E', 0 => 'F' )
How can we determine the lowest (non negative) integer of the first empty key, without re-indexing the array? In this case it would be 4.
Easy solution by while loop:
function firstFreeKey($array)
{
$i = 0;
while(isset($array[$i])) $i++;
return $i;
}
$array = array( 1 => 'A', 5 => 'B', 2 => 'C', 3 => 'D', 6 => 'E', 0 => 'F' );
echo firstFreeKey($array);
Output:
4
$a = array( 1 => 'A', 5 => 'B', 2 => 'C', 3 => 'D', 6 => 'E', 0 => 'F' );
for($i = 0, $l = count($a); $i < $l; $i++) if(!array_key_exists($i, $a)) break;
echo $i; // 4
First method is getting fancy with arrays.
// STUPID FANCY WAY (playing with arrays)
$array = array( 1 => 'A', 5 => 'B', 2 => 'C', 3 => 'D', 6 => 'E', 0 => 'F' );
// fetch existing keys
$keys = array_keys($array);
// build a range with all keys (between min and max)
$keys_all = range(min($keys), max($keys));
// difference between all and existing
$keys_diff = array_diff($keys_all, $keys);
// fetch the first in difference (min)
$min_key = reset($keys_diff);
// output it
var_dump($min_key);
Second method is actually viable as it spares the need for extra arrays in memory.
// PROPER WAY (not extra array operations)
$array = array( 1 => 'A', 5 => 'B', 2 => 'C', 3 => 'D', 6 => 'E', 0 => 'F' );
// fetch existing keys
$keys = array_keys($array);
// nullify first
$min_key = null;
// loop all keys and find missing one (skip 1st and last, they totally exist!)
for($key = ($min = min($keys)) + 1, $max = max($keys); $key < $max; ++$key){
if(!isset($array[$key])){
$min_key = $key;
break;
}
}
// output it
var_dump($min_key);
I'll explain if comments are not enough.
PS: This method handles situation when minimum key value is not 0. Hence it's independent of range min and max. And keys need to be integers, obviously.
Have you tried something like
echo min(array_keys($array));
array_keys returns the list of keys for the array, and min determines the lowest value in the array.
Expanding following comments :
As the array is a continous int value, you can do the following
$keys = array_keys($array);
$arr2 = range(0,max($keys));
// diff gets the missing elements
$missing = array_diff($arr2,$keys);
echo $missing[0];
Not sure on the performance of this vs brute force, but it can be tested.
Possibly one of the longest answers, but the most explained.
$Jumbled_Arr = array(
1 => "test",
3 => "another",
4 => "t",
7 => "kfkfk",
9 => "fk"
);
function GetMissingKey($Array,$Return_Type = false){
// Get the Maximum Key Value
$Max_Key = max(array_keys($Array));
// Treat this as the default array index, as all numerical arrays start at a 0 key set
$Counter = 0;
// A Blank array to be maniuplated
$Generated_Array = array();
// Generate a full array based on the maximum value and the array index, this will create a keyset from 0 to the highest value
while ($Counter < $Max_Key){
$Generated_Array[] = $Counter;
$Counter++;
}
// Discover the differences between the correctly formed array and the specified array keys
$Missing_Key = array_diff($Generated_Array,array_keys($Array));
// Work with the second parameter, as the settings.
if ($Return_Type === true){
// If set to true, we are expecting more than 1 free key within the array, so we will return the entire array
return $Missing_Key;
}elseif ($Return_Type === false){
// If set to false (or blank) we are expecting a single free key to be discovered & we will return it as an imploded array
return implode($Missing_Key);
}
}
print_R(GetMissingKey($Jumbled_Arr,true));
If true is passed through to this function, you will be returned with an array of missing keys.
If false is passed through, then you will be returned with a single integer (careful when if expecting more than one key to be returned).
$foo = array(1 => 'A', 5 => 'B', 2 => 'C', 3 => 'D', 6 => 'E', 0 => 'F');
$bar = array(1 => 'A', 4 => 'Z', 2 => 'L', 3 => 'N');
function firstFreeKey($array)
{
ksort($array);
for ($i = 0; $i <= count($array); $i++) {
if (!isset($array[$i])) {
return $i;
}
}
return count($array) + 1;
}
function firstFreeKeyNotZero($array)
{
$i = min(array_keys($array));
while (isset($array[$i])) {
$i++;
}
return $i;
}
echo firstFreeKey($foo); //returns 4
echo firstFreeKey($bar); //returns 0
echo firstFreeKeyNotZero($foo); //returns 4
echo firstFreeKeyNotZero($bar); //returns 5
Edit: Added case where not starting from 0, as requested by CodeAngry
To get all free keys:
$aArray = array(1 => 'A', 5 => 'B', 2 => 'C', 3 => 'D', 6 => 'E');
$iMaxKey = 7; //max key you will need
$aAllKeys = range(0, iMaxKey); //create array(0,1,2,3,4,5,6,7);
$aAllFreeKeys = array_diff_key($aAllKeys, $aArray); //will be: array(0,4,7);
and now if you need first free key:
$iKey = array_shift($aAllFreeKeys);
I've been searching for this for some days but cant seem to find the anwer.
I have an array like
$startArray = Array(array('name' => 'A', 'hierarchy' => '1'),
array('name' => 'B', 'hierarchy' => '1.2'),
array('name' => 'C', 'hierarchy' => '1.3'),
array('name' => 'D', 'hierarchy' => '1.3.1')
);
and i would like to go to
$endResult =
array(
array('name' => 'A',
'children' => array(
array('name'=> 'b'),
array('name'=> 'c',
'children' => array('name' => 'D')
)
)
);
Any suggestions?
Thanks
Okay here is a long solution, which you might want to wrap into a function, perhaps. Just copy and paste this inside a blank document and check the result in a browser.
<pre>
<?php
$orig = array(
array('name' => 'A', 'hierarchy' => '1'),
array('name' => 'B', 'hierarchy' => '1.2'),
array('name' => 'C', 'hierarchy' => '1.3'),
array('name' => 'D', 'hierarchy' => '1.3.1')
//,array('name' => 'E', 'hierarchy' => '1.2.1')
//,array('name' => 'F', 'hierarchy' => '2.1')
);
function special_sort($arr1,$arr2) {
$h1 = $arr1['hierarchy'];
$h2 = $arr2['hierarchy'];
$ch1 = count($h1);
$ch2 = count($h2);
if ($ch1 < $ch2) return -1;
if ($ch1 > $ch2) return 1;
return $h1 > $h2 ? 1 : ($h1 < $h2 ? -1 : 0);
}
// this first checks lengths and then values
// so 1.3 gets -1 against 1.2.1 whereas
// 1.3.2 still gets 1 against 1.3.1
$temp = $orig;
// temporary array to keep your original untouched
foreach ($temp as &$arr) {
$arr['hierarchy'] = explode('.',$arr['hierarchy']);
// turn hierachy numbers into arrays for later
// sorting purposes
}
unset($arr);
// get rid of the reference used in the loop
usort($temp,'special_sort');
// sort by the sort function above
echo '<h2>$orig</h2>'; print_r($orig);
echo '<h2>$temp</h2>'; print_r($temp);
// for you to see what it looks like now
$res = array();
// our final array
// by the following loop we're using the hierarcy
// numbers as keys to push into the result array
foreach ($temp as $arr) {
$h = $arr['hierarchy'];
if (count($h) == 1) {
// this is for those with single hierarchy
// numbers such as 1, 2, etc.
$res[$h[0]] = array('name'=>$arr['name']);
continue;
}
if (!isset($res[$h[0]]))
$res[$h[0]] = array('name'=>'');
// if, say this is 2.1 but there is no 2 in the array then
// we need to create that. see the last commented item in
// the original array. uncomment it to see why I wrote this
$newLoc =& $res[$h[0]];
// reference of the new place in result
// array to push the item
for ($i = 1; $i < count($h); $i++) {
if (!isset($newLoc['children']))
$newLoc['children'] = array();
// create children array if it doesn't exist
if (!isset($newLoc['children'][$h[$i]]))
$newLoc['children'][$h[$i]] = array();
// create children[hierarch key] array if it doesn't exist
$newLoc =& $newLoc['children'][$h[$i]];
// update reference
}
$newLoc = array('name'=>$arr['name']);
// assign the new name to this reference
unset($newLoc);
// get rid of the reference
}
unset($temp);
// get rid of the array now that we're done with it
echo '<h2>$res</h2>'; print_r($res);
function fix_keys($array) {
foreach ($array as $k => $val) {
if (is_array($val)) $array[$k] = fix_keys($val);
}
if(is_numeric($k)) return array_values($array);
return $array;
}
// function courtesy of Lobos,
// http://stackoverflow.com/a/12399408/913097
$endRes = fix_keys($res);
// create the end result array. this is actually
// a copy of the $res array except the numeric
// keys were reset.
unset($res);
// get rid of the last unused array
echo '<h2>$endRes</h2>'; print_r($endRes);
?>
</pre>
you do not have the correct array, the values will be rewritten. You need approximately the:
$arr = array(
array('name' => 'a', 'hierarchy' => '1'),
array('name' => 'b', 'hierarchy' => '1.2'),
);
I want to sort arrays by key in php, but the alphabet that I'm using is not the normal English alphabet -- it's a self-created alphabet. Is this possible?
My alphabet is:
$alphabet = "AjawbpfmnrhHxXsSqkgtTdD =";
The array is like this:
Array (
[=k_0] => Array(
[0] => DI.3,2 &dwA-nTr& #Hrw#
[1] => mA
[2] => =k
[3] => Sfj,t
[4] => =k
[5] => pXr
)
[aA_2] => Array(
[0] => DI.7,4 &dwA-nTr& #Hrw-smA-tA,wj#
[1] => snD
[2] => aA
[3] => Sfj,t
[4] => jt
[5] => jt,w
)
[sqA_1] => Array(
[0] => DI.6,18 &dwA-nTr& #nswt#
[1] => ra
[2] => sqA
[3] => Sfj,t
[4] => =s
[5] => r
)
);
So if I sort this array following my alphabet then the array with the key [=k_0] should be at the end.
You can use the usort() function and provide your own sorting logic.
See php.net for an example.
Edit: use uksort, not usort. See http://www.php.net/manual/en/function.uksort.php. Thanks #Darien!
A slightly modified example from php.net - the original code with an $alphabet mapping added:
function cmp($a, $b)
{
// custom sort order - just swapps 2 and 3.
$alphabet = array (1 => 1, 2 => 3, 3 => 2, 4 => 4, 5 => 5, 6=> 6);
if ($alphabet[$a] == $alphabet[$b]) {
return 0;
}
return ($alphabet[$a] < $alphabet[$b]) ? -1 : 1;
}
$a = array(3 => 'c' , 2 => 'b', 5 => 'e', 6 => 'f', 1=>'a');
uksort($a, "cmp");
foreach ($a as $key => $value) {
echo "$key: $value\n";
}
Given your $alphabet = "AjawbpfmnrhHxXsSqkgtTdD";, and assuming that A<j<a, etc, per your comment, transform each key in the alternative alphabet to a series in the known alphabet, e.g. use a mapping like:
your alphabet: AjawbpfmnrhHxXsSqkgtTdD
'real'alphabet: abcdefghijklmnopqrstuvw
So the key 'Ajaw' => 'abcd', and 'fmnr' => 'ghij', etc. This then turns your keys into something you can sort using conventional php functions. You'd need some way to handle characeters not present in your original alphabet though.
Something like that might work - you'd need two transform functions (from your alphabet to 'real' alphabet and vice versa), and then a comparator for e.g. uksort.
My two cents - thanks for clarifying the original question.
Fortunately, your custom alphabet does not have more characters than the list of single-byte latin letters, so translating is a very simple and readable process.
I recommend that you set up a translation array before you begin sorting, then translate/normalize the keys for the purpose of sorting.
Code: (Demo)
$array = [
'=k_0' => ['test1'],
'aA_2' => ['test2'],
'sqA_1' => ['test3'],
'=kj_0' => ['test4'],
'awA_2' => ['test5'],
'= D_1' => ['test6'],
'sq A_1' => ['test7'],
'sqA_2' => ['test8'],
];
$trans = ['AjawbpfmnrhHxXsSqkgtTdD =', 'abcdefghijklmnopqrstuvwxy'];
uksort(
$array,
function ($a, $b) use ($trans) {
return strtr($a, ...$trans) <=> strtr($b, ...$trans);
}
);
var_export($array);
Output:
array (
'aA_2' =>
array (
0 => 'test2',
),
'awA_2' =>
array (
0 => 'test5',
),
'sqA_1' =>
array (
0 => 'test3',
),
'sqA_2' =>
array (
0 => 'test8',
),
'sq A_1' =>
array (
0 => 'test7',
),
'=k_0' =>
array (
0 => 'test1',
),
'=kj_0' =>
array (
0 => 'test4',
),
'= D_1' =>
array (
0 => 'test6',
),
)
From PHP7.4, the syntax can be reduced using arrow function syntax.
uksort(
$array,
fn($a, $b) => strtr($a, ...$trans) <=> strtr($b, ...$trans)
);
After feedback from #mickmackusa I have updated my example to work with uksrot and answer the question fully
$order = str_split("AjawbpfmnrhHxXsSqkgtTdD");
uksort($arr, function ($a, $b) use ($order) {
$posA = array_search($a, $order);
$posB = array_search($b, $order);
return $posA - $posB;
});
http://sandbox.onlinephpfunctions.com/code/9b6f39b30dcc932517bbe82608dd8a0c8d35b3da
-- original response with usort--
You can use usort() with a custom order array like so
$arr = array("w","b","m","n","x","x","z","T","T","A","A");
$order = array("A","j","a","w","b","p","f","m","n","r","h","H","x","X","s","S","q","k","g","t","T","d","D"," ","=");
usort($arr, function ($a, $b) use ($order) {
$posA = array_search($a, $order);
$posB = array_search($b, $order);
return $posA - $posB;
});
(you could probably just explode the $order string so it's nicer)
I actually just came across this answer which explains it better, and handles if a value is not inside the order array.
And a working example http://sandbox.onlinephpfunctions.com/code/3934aafe93377ec18549d326d6551608436242a7
<?php
$arr = [8,10,12,18,20,7,4,6,2,20,0]; //take array
$a= sortasc($arr); // call function
function sortasc($arr){
for($i=0;$i<=count($arr);$i++){
for($j=1;$j<=count($arr)-1;$j++){
if($arr[$j-1]>$arr[$j]){
$temp = $arr[$j];
$arr[$j]= $arr[$j-1];
$arr[$j-1] = $temp;
}
}
}
return $arr;
}
?>
See this code:
<?php
$arr = array('wr' => 1, 'wrS' => 6, 'wr,w' => 3, 'wr.w' => 4, 'wr-qA' => 2, 'wrs' => 5);
function compare_by_alphabet(array $alphabet, $str1, $str2)
{
$l1 = strlen($str1);
$l2 = strlen($str2);
$c = min($l1, $l2);
for ($i = 0; $i < $c; $i++)
{
$s1 = $str1[$i];
$s2 = $str2[$i];
if ($s1===$s2) continue;
$i1 = array_search($s1, $alphabet);
if ($i1===false) continue;
$i2 = array_search($s2, $alphabet);
if ($i2===false) continue;
if ($i2===$i1) continue;
if ($i1 < $i2) return -1;
else return 1;
}
if ($l1 < $l2) return -1;
elseif ($l1 > $l2) return 1;
return 0;
}
function compare_keys_by_alphabet($a, $b)
{
static $alphabet = array('-', ',', '.', 'A', 'j', 'a', 'w', 'b', 'p', 'f', 'm', 'n', 'r', 'h', 'H', 'x', 'X', 's', 'S', 'q', 'k', 'g', 't', 'T', 'd', 'D', '=', '/', '(', ')', '[', ']', '<', '>', '{', '}', '\'', '*', '#', 'I', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, '&', '#');
return compare_by_alphabet($alphabet, $a, $b);
}
uksort($arr, 'compare_keys_by_alphabet');
print_r($arr);
Result:
Array
(
[wr] => 1
[wr-qA] => 2
[wr,w] => 3
[wr.w] => 4
[wrs] => 5
[wrS] => 6
)
Let's imagine that we have two arrays:
$array_1 = array(
'0' => 'zero',
'1' => 'one',
'2' => 'two',
'3' => 'three',
);
$array_2 = array(
'zero' => '0',
'one' => '1',
'two' => '2',
'three' => '3',
);
Now, I'd like to insert array('sample_key' => 'sample_value') after third element of each array. How can I do it?
array_slice() can be used to extract parts of the array, and the union array operator (+) can recombine the parts.
$res = array_slice($array, 0, 3, true) +
array("my_key" => "my_value") +
array_slice($array, 3, count($array)-3, true);
This example:
$array = array(
'zero' => '0',
'one' => '1',
'two' => '2',
'three' => '3',
);
$res = array_slice($array, 0, 3, true) +
array("my_key" => "my_value") +
array_slice($array, 3, count($array) - 1, true) ;
print_r($res);
gives:
Array
(
[zero] => 0
[one] => 1
[two] => 2
[my_key] => my_value
[three] => 3
)
For your first array, use array_splice():
$array_1 = array(
'0' => 'zero',
'1' => 'one',
'2' => 'two',
'3' => 'three',
);
array_splice($array_1, 3, 0, 'more');
print_r($array_1);
output:
Array(
[0] => zero
[1] => one
[2] => two
[3] => more
[4] => three
)
for the second one there is no order so you just have to do :
$array_2['more'] = '2.5';
print_r($array_2);
And sort the keys by whatever you want.
code:
function insertValueAtPosition($arr, $insertedArray, $position) {
$i = 0;
$new_array=[];
foreach ($arr as $key => $value) {
if ($i == $position) {
foreach ($insertedArray as $ikey => $ivalue) {
$new_array[$ikey] = $ivalue;
}
}
$new_array[$key] = $value;
$i++;
}
return $new_array;
}
example:
$array = ["A"=8, "K"=>3];
$insert_array = ["D"= 9];
insertValueAtPosition($array, $insert_array, $position=2);
// result ====> ["A"=>8, "D"=>9, "K"=>3];
May not really look perfect, but it works.
Here's a simple function that you could use. Just plug n play.
This is Insert By Index, Not By Value.
you can choose to pass the array, or use one that you already have declared.
Newer, shorter version:
function insert($array, $index, $val)
{
$size = count($array); //because I am going to use this more than one time
if (!is_int($index) || $index < 0 || $index > $size)
{
return -1;
}
else
{
$temp = array_slice($array, 0, $index);
$temp[] = $val;
return array_merge($temp, array_slice($array, $index, $size));
}
}
Older, longer version:
function insert($array, $index, $val) { //function decleration
$temp = array(); // this temp array will hold the value
$size = count($array); //because I am going to use this more than one time
// Validation -- validate if index value is proper (you can omit this part)
if (!is_int($index) || $index < 0 || $index > $size) {
echo "Error: Wrong index at Insert. Index: " . $index . " Current Size: " . $size;
echo "<br/>";
return false;
}
//here is the actual insertion code
//slice part of the array from 0 to insertion index
$temp = array_slice($array, 0, $index);//e.g index=5, then slice will result elements [0-4]
//add the value at the end of the temp array// at the insertion index e.g 5
array_push($temp, $val);
//reconnect the remaining part of the array to the current temp
$temp = array_merge($temp, array_slice($array, $index, $size));
$array = $temp;//swap// no need for this if you pass the array cuz you can simply return $temp, but, if u r using a class array for example, this is useful.
return $array; // you can return $temp instead if you don't use class array
}
Usage example:
//1
$result = insert(array(1,2,3,4,5),0, 0);
echo "<pre>";
echo "<br/>";
print_r($result);
echo "</pre>";
//2
$result = insert(array(1,2,3,4,5),2, "a");
echo "<pre>";
print_r($result);
echo "</pre>";
//3
$result = insert(array(1,2,3,4,5) ,4, "b");
echo "<pre>";
print_r($result);
echo "</pre>";
//4
$result = insert(array(1,2,3,4,5),5, 6);
echo "<pre>";
echo "<br/>";
print_r($result);
echo "</pre>";
Expected result:
//1
Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
[5] => 5
)
//2
Array
(
[0] => 1
[1] => 2
[2] => a
[3] => 3
[4] => 4
[5] => 5
)
//3
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => b
[5] => 5
)
//4
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
)
$list = array(
'Tunisia' => 'Tunis',
'Germany' => 'Berlin',
'Italy' => 'Rom',
'Egypt' => 'Cairo'
);
$afterIndex = 2;
$newVal= array('Palestine' => 'Jerusalem');
$newList = array_merge(array_slice($list,0,$afterIndex+1), $newVal,array_slice($list,$afterIndex+1));
This function supports:
both numeric and assoc keys
insert before or after the founded key
append to the end of array if key isn't founded
function insert_into_array( $array, $search_key, $insert_key, $insert_value, $insert_after_founded_key = true, $append_if_not_found = false ) {
$new_array = array();
foreach( $array as $key => $value ){
// INSERT BEFORE THE CURRENT KEY?
// ONLY IF CURRENT KEY IS THE KEY WE ARE SEARCHING FOR, AND WE WANT TO INSERT BEFORE THAT FOUNDED KEY
if( $key === $search_key && ! $insert_after_founded_key )
$new_array[ $insert_key ] = $insert_value;
// COPY THE CURRENT KEY/VALUE FROM OLD ARRAY TO A NEW ARRAY
$new_array[ $key ] = $value;
// INSERT AFTER THE CURRENT KEY?
// ONLY IF CURRENT KEY IS THE KEY WE ARE SEARCHING FOR, AND WE WANT TO INSERT AFTER THAT FOUNDED KEY
if( $key === $search_key && $insert_after_founded_key )
$new_array[ $insert_key ] = $insert_value;
}
// APPEND IF KEY ISNT FOUNDED
if( $append_if_not_found && count( $array ) == count( $new_array ) )
$new_array[ $insert_key ] = $insert_value;
return $new_array;
}
USAGE:
$array1 = array(
0 => 'zero',
1 => 'one',
2 => 'two',
3 => 'three',
4 => 'four'
);
$array2 = array(
'zero' => '# 0',
'one' => '# 1',
'two' => '# 2',
'three' => '# 3',
'four' => '# 4'
);
$array3 = array(
0 => 'zero',
1 => 'one',
64 => '64',
3 => 'three',
4 => 'four'
);
// INSERT AFTER WITH NUMERIC KEYS
print_r( insert_into_array( $array1, 3, 'three+', 'three+ value') );
// INSERT AFTER WITH ASSOC KEYS
print_r( insert_into_array( $array2, 'three', 'three+', 'three+ value') );
// INSERT BEFORE
print_r( insert_into_array( $array3, 64, 'before-64', 'before-64 value', false) );
// APPEND IF SEARCH KEY ISNT FOUNDED
print_r( insert_into_array( $array3, 'undefined assoc key', 'new key', 'new value', true, true) );
RESULTS:
Array
(
[0] => zero
[1] => one
[2] => two
[3] => three
[three+] => three+ value
[4] => four
)
Array
(
[zero] => # 0
[one] => # 1
[two] => # 2
[three] => # 3
[three+] => three+ value
[four] => # 4
)
Array
(
[0] => zero
[1] => one
[before-64] => before-64 value
[64] => 64
[3] => three
[4] => four
)
Array
(
[0] => zero
[1] => one
[64] => 64
[3] => three
[4] => four
[new key] => new value
)
Simplest solution, if you want to insert (an element or array) after a certain key:
function array_splice_after_key($array, $key, $array_to_insert)
{
$key_pos = array_search($key, array_keys($array));
if($key_pos !== false){
$key_pos++;
$second_array = array_splice($array, $key_pos);
$array = array_merge($array, $array_to_insert, $second_array);
}
return $array;
}
So, if you have:
$array = [
'one' => 1,
'three' => 3
];
$array_to_insert = ['two' => 2];
And execute:
$result_array = array_splice_after_key($array, 'one', $array_to_insert);
You'll have:
Array (
['one'] => 1
['two'] => 2
['three'] => 3
)
I recently wrote a function to do something similar to what it sounds like you're attempting, it's a similar approach to clasvdb's answer.
function magic_insert($index,$value,$input_array ) {
if (isset($input_array[$index])) {
$output_array = array($index=>$value);
foreach($input_array as $k=>$v) {
if ($k<$index) {
$output_array[$k] = $v;
} else {
if (isset($output_array[$k]) ) {
$output_array[$k+1] = $v;
} else {
$output_array[$k] = $v;
}
}
}
} else {
$output_array = $input_array;
$output_array[$index] = $value;
}
ksort($output_array);
return $output_array;
}
Basically it inserts at a specific point, but avoids overwriting by shifting all items down.
Using array_splice instead of array_slice gives one less function call.
$toto = array(
'zero' => '0',
'one' => '1',
'two' => '2',
'three' => '3'
);
$ret = array_splice($toto, 3 );
$toto = $toto + array("my_key" => "my_value") + $ret;
print_r($toto);
If you don't know that you want to insert it at position #3, but you know the key that you want to insert it after, I cooked up this little function after seeing this question.
/**
* Inserts any number of scalars or arrays at the point
* in the haystack immediately after the search key ($needle) was found,
* or at the end if the needle is not found or not supplied.
* Modifies $haystack in place.
* #param array &$haystack the associative array to search. This will be modified by the function
* #param string $needle the key to search for
* #param mixed $stuff one or more arrays or scalars to be inserted into $haystack
* #return int the index at which $needle was found
*/
function array_insert_after(&$haystack, $needle = '', $stuff){
if (! is_array($haystack) ) return $haystack;
$new_array = array();
for ($i = 2; $i < func_num_args(); ++$i){
$arg = func_get_arg($i);
if (is_array($arg)) $new_array = array_merge($new_array, $arg);
else $new_array[] = $arg;
}
$i = 0;
foreach($haystack as $key => $value){
++$i;
if ($key == $needle) break;
}
$haystack = array_merge(array_slice($haystack, 0, $i, true), $new_array, array_slice($haystack, $i, null, true));
return $i;
}
Here's a codepad fiddle to see it in action: http://codepad.org/5WlKFKfz
Note: array_splice() would have been a lot more efficient than array_merge(array_slice()) but then the keys of your inserted arrays would have been lost. Sigh.
Cleaner approach (based on fluidity of use and less code).
/**
* Insert data at position given the target key.
*
* #param array $array
* #param mixed $target_key
* #param mixed $insert_key
* #param mixed $insert_val
* #param bool $insert_after
* #param bool $append_on_fail
* #param array $out
* #return array
*/
function array_insert(
array $array,
$target_key,
$insert_key,
$insert_val = null,
$insert_after = true,
$append_on_fail = false,
$out = [])
{
foreach ($array as $key => $value) {
if ($insert_after) $out[$key] = $value;
if ($key == $target_key) $out[$insert_key] = $insert_val;
if (!$insert_after) $out[$key] = $value;
}
if (!isset($array[$target_key]) && $append_on_fail) {
$out[$insert_key] = $insert_val;
}
return $out;
}
Usage:
$colors = [
'blue' => 'Blue',
'green' => 'Green',
'orange' => 'Orange',
];
$colors = array_insert($colors, 'blue', 'pink', 'Pink');
die(var_dump($colors));
This is an old question, but I posted a comment in 2014 and frequently come back to this. I thought I would leave a full answer. This isn't the shortest solution but it is quite easy to understand.
Insert a new value into an associative array, at a numbered position, preserving keys, and preserving order.
$columns = array(
'id' => 'ID',
'name' => 'Name',
'email' => 'Email',
'count' => 'Number of posts'
);
$columns = array_merge(
array_slice( $columns, 0, 3, true ), // The first 3 items from the old array
array( 'subscribed' => 'Subscribed' ), // New value to add after the 3rd item
array_slice( $columns, 3, null, true ) // Other items after the 3rd
);
print_r( $columns );
/*
Array (
[id] => ID
[name] => Name
[email] => Email
[subscribed] => Subscribed
[count] => Number of posts
)
*/
I do that as
$slightly_damaged = array_merge(
array_slice($slightly_damaged, 0, 4, true) + ["4" => "0.0"],
array_slice($slightly_damaged, 4, count($slightly_damaged) - 4, true)
);
This is another solution in PHP 7.1
/**
* #param array $input Input array to add items to
* #param array $items Items to insert (as an array)
* #param int $position Position to inject items from (starts from 0)
*
* #return array
*/
function arrayInject( array $input, array $items, int $position ): array
{
if (0 >= $position) {
return array_merge($items, $input);
}
if ($position >= count($input)) {
return array_merge($input, $items);
}
return array_merge(
array_slice($input, 0, $position, true),
$items,
array_slice($input, $position, null, true)
);
}
I just created an ArrayHelper class that would make this very easy for numeric indexes.
class ArrayHelper
{
/*
Inserts a value at the given position or throws an exception if
the position is out of range.
This function will push the current values up in index. ex. if
you insert at index 1 then the previous value at index 1 will
be pushed to index 2 and so on.
$pos: The position where the inserted value should be placed.
Starts at 0.
*/
public static function insertValueAtPos(array &$array, $pos, $value) {
$maxIndex = count($array)-1;
if ($pos === 0) {
array_unshift($array, $value);
} elseif (($pos > 0) && ($pos <= $maxIndex)) {
$firstHalf = array_slice($array, 0, $pos);
$secondHalf = array_slice($array, $pos);
$array = array_merge($firstHalf, array($value), $secondHalf);
} else {
throw new IndexOutOfBoundsException();
}
}
}
Example:
$array = array('a', 'b', 'c', 'd', 'e');
$insertValue = 'insert';
\ArrayHelper::insertValueAtPos($array, 3, $insertValue);
Beginning $array:
Array (
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
)
Result:
Array (
[0] => a
[1] => b
[2] => c
[3] => insert
[4] => d
[5] => e
)
This is better method how insert item to array on some position.
function arrayInsert($array, $item, $position)
{
$begin = array_slice($array, 0, $position);
array_push($begin, $item);
$end = array_slice($array, $position);
$resultArray = array_merge($begin, $end);
return $resultArray;
}
I needed something that could do an insert before, replace, after the key; and add at the start or end of the array if target key is not found. Default is to insert after the key.
New Function
/**
* Insert element into an array at a specific key.
*
* #param array $input_array
* The original array.
* #param array $insert
* The element that is getting inserted; array(key => value).
* #param string $target_key
* The key name.
* #param int $location
* 1 is after, 0 is replace, -1 is before.
*
* #return array
* The new array with the element merged in.
*/
function insert_into_array_at_key(array $input_array, array $insert, $target_key, $location = 1) {
$output = array();
$new_value = reset($insert);
$new_key = key($insert);
foreach ($input_array as $key => $value) {
if ($key === $target_key) {
// Insert before.
if ($location == -1) {
$output[$new_key] = $new_value;
$output[$key] = $value;
}
// Replace.
if ($location == 0) {
$output[$new_key] = $new_value;
}
// After.
if ($location == 1) {
$output[$key] = $value;
$output[$new_key] = $new_value;
}
}
else {
// Pick next key if there is an number collision.
if (is_numeric($key)) {
while (isset($output[$key])) {
$key++;
}
}
$output[$key] = $value;
}
}
// Add to array if not found.
if (!isset($output[$new_key])) {
// Before everything.
if ($location == -1) {
$output = $insert + $output;
}
// After everything.
if ($location == 1) {
$output[$new_key] = $new_value;
}
}
return $output;
}
Input code
$array_1 = array(
'0' => 'zero',
'1' => 'one',
'2' => 'two',
'3' => 'three',
);
$array_2 = array(
'zero' => '0',
'one' => '1',
'two' => '2',
'three' => '3',
);
$array_1 = insert_into_array_at_key($array_1, array('sample_key' => 'sample_value'), 2, 1);
print_r($array_1);
$array_2 = insert_into_array_at_key($array_2, array('sample_key' => 'sample_value'), 'two', 1);
print_r($array_2);
Output
Array
(
[0] => zero
[1] => one
[2] => two
[sample_key] => sample_value
[3] => three
)
Array
(
[zero] => 0
[one] => 1
[two] => 2
[sample_key] => sample_value
[three] => 3
)
Very simple 2 string answer to your question:
$array_1 = array(
'0' => 'zero',
'1' => 'one',
'2' => 'two',
'3' => 'three',
);
At first you insert anything to your third element with array_splice and then assign a value to this element:
array_splice($array_1, 3, 0 , true);
$array_1[3] = array('sample_key' => 'sample_value');
Not as concrete as the answer of Artefacto, but based in his suggestion of using array_slice(), I wrote the next function:
function arrayInsert($target, $byKey, $byOffset, $valuesToInsert, $afterKey) {
if (isset($byKey)) {
if (is_numeric($byKey)) $byKey = (int)floor($byKey);
$offset = 0;
foreach ($target as $key => $value) {
if ($key === $byKey) break;
$offset++;
}
if ($afterKey) $offset++;
} else {
$offset = $byOffset;
}
$targetLength = count($target);
$targetA = array_slice($target, 0, $offset, true);
$targetB = array_slice($target, $offset, $targetLength, true);
return array_merge($targetA, $valuesToInsert, $targetB);
}
Features:
Inserting one or múltiple values
Inserting key value pair(s)
Inserting before/after the key, or by offset
Usage examples:
$target = [
'banana' => 12,
'potatoe' => 6,
'watermelon' => 8,
'apple' => 7,
2 => 21,
'pear' => 6
];
// Values must be nested in an array
$insertValues = [
'orange' => 0,
'lemon' => 3,
3
];
// By key
// Third parameter is not applicable
// Insert after 2 (before 'pear')
var_dump(arrayInsert($target, 2, null, $valuesToInsert, true));
// Insert before 'watermelon'
var_dump(arrayInsert($target, 'watermelon', null, $valuesToInsert, false));
// By offset
// Second and last parameter are not applicable
// Insert in position 2 (zero based i.e. before 'watermelon')
var_dump(arrayInsert($target, null, 2, $valuesToInsert, null));
In case you are just looking to insert an item into an array at a certain position (based on #clausvdb answer):
function array_insert($arr, $insert, $position) {
$i = 0;
$ret = array();
foreach ($arr as $key => $value) {
if ($i == $position) {
$ret[] = $insert;
}
$ret[] = $value;
$i++;
}
return $ret;
}
Here is my version:
/**
*
* Insert an element after an index in an array
* #param array $array
* #param string|int $key
* #param mixed $value
* #param string|int $offset
* #return mixed
*/
function array_splice_associative($array, $key, $value, $offset) {
if (!is_array($array)) {
return $array;
}
if (array_key_exists($key, $array)) {
unset($array[$key]);
}
$return = array();
$inserted = false;
foreach ($array as $k => $v) {
$return[$k] = $v;
if ($k == $offset && !$inserted) {
$return[$key] = $value;
$inserted = true;
}
}
if (!$inserted) {
$return[$key] = $value;
}
return $return;
}
try this one ===
$key_pos=0;
$a1=array("a"=>"red", "b"=>"green", "c"=>"blue", "d"=>"yellow");
$arrkey=array_keys($a1);
array_walk($arrkey,function($val,$key) use(&$key_pos) {
if($val=='b')
{
$key_pos=$key;
}
});
$a2=array("e"=>"purple");
$newArray = array_slice($a1, 0, $key_pos, true) + $a2 +
array_slice($a1, $key_pos, NULL, true);
print_r($newArray);
Output
Array (
[a] => red
[e] => purple
[b] => green
[c] => blue
[d] => yellow )
This can be done by array.splice(). Please note array_splice or array_merge doesn't preserve keys for associative arrays. So array_slice is used and '+' operator is used for concatenating the two arrays.
More details here
$array_1 = array(
'0' => 'zero',
'1' => 'one',
'2' => 'two',
'3' => 'three',
);
$array_2 = array(
'zero' => '0',
'one' => '1',
'two' => '2',
'three' => '3',
);
$index = 2;
$finalArray = array_slice($array_1, 0, $index, true) +
$array2 +
array_slice($array_2, $index, NULL, true);
print_r($finalArray);
/*
Array
(
[0] => zero
[1] => one
[10] => grapes
[z] => mangoes
[two] => 2
[three] => 3
)
*/
I've created a function (PHP 8.1), which allows you to insert items to associative or numeric arrays:
function insertItemsToPosition(array $array, string|int $insertAfterPosition, array $itemsToAdd): array
{
$insertAfterIndex = array_search($insertAfterPosition, array_keys($array), true);
if ($insertAfterIndex === false) {
throw new \UnexpectedValueException(sprintf('You try to insert items to an array after the key "%s", but this key is not existing in given array. Available keys are: %s', $insertAfterPosition, implode(', ', array_keys($array))));
}
$itemsBefore = array_slice($array, 0, $insertAfterIndex + 1);
$itemsAfter = array_slice($array, $insertAfterIndex + 1);
return $itemsBefore + $itemsToAdd + $itemsAfter;
}
You can insert elements during a foreach loop, since this loop works on a copy of the original array, but you have to keep track of the number of inserted lines (I call this "bloat" in this code):
$bloat=0;
foreach ($Lines as $n=>$Line)
{
if (MustInsertLineHere($Line))
{
array_splice($Lines,$n+$bloat,0,"string to insert");
++$bloat;
}
}
Obviously, you can generalize this "bloat" idea to handle arbitrary insertions and deletions during the foreach loop.