Split string on multiple characters in PHP - php

I need to split an age into its components where the age is expressed as eg. 27y5m6w2d or any combination of those values. eg. 2w3d or 27d or 5y2d etc. Result has to be up to 4 variables $yrs, $mths, $wks and $days containing the appropriate numeric values.
I can do it with this code but am hoping there is something more efficient:
$pos = strpos($age, 'y');
if ($pos !== false)
list($yrs, $age) = explode('y', $age);
$pos = strpos($age, 'm');
if ($pos !== false)
list($mths, $age) = explode('m', $age);
$pos = strpos($age, 'w');
if ($pos !== false)
list($wks, $age) = explode('w', $age);
$pos = strpos($age, 'd');
if ($pos !== false)
list($days, $age) = explode('d', $age);
If you have a suggestion, please run it in a 10,000 iteration loop and advise the results. The code above runs in an average of 0.06 seconds for 10,000 iterations. I use this code to test:
<?php
$startTime = microtime(true);
// code goes here
echo "Time: " . number_format(( microtime(true) - $startTime), 4) . " Seconds<br>";
echo 'y='.$yrs.' m='.$mths.' w='.$wks.' d='.$days;
?>

I would suggest using regular expression matching with preg_match_all() like this:
$input = '2w3d'
$matches = array();
preg_match_all('|(\d+)([ymwd])|', $input, $matches, PREG_SET_ORDER);
Where the output array $matches will hold all matches in this pattern:
$matches = array(
// 0 => matched string, 1 => first capture group, 2 => second capture group
0 => array( 0 => '2w', 1 => '2', 2 => 'w' ),
1 => array( 0 => '3d', 1 => '3', 2 => 'd' )
);
EDIT :
Process this result like so:
$yrs = $mths = $wks = $days = 0;
foreach($matches as $match) {
switch($match[2]) {
case 'y': $yrs = (int)$match[1]; break;
case 'm': $mths = (int)$match[1]; break;
case 'w': $wkss = (int)$match[1]; break;
case 'd': $days = (int)$match[1]; break;
}
}
EDIT 2: Hacky alternative
Makes use of character comparison and takes around 0.4 seconds for 100.000 iterations.
$number = '';
for($j = 0, $length = strlen($input); $j < $length; $j++) {
if($input[$j] < 'A') {
$number .= $input[$j];
} else {
switch($input[$j]) {
case 'y': $yrs = (int)$number; break;
case 'm': $mths = (int)$number; break;
case 'w': $wks = (int)$number; break;
case 'd': $days = (int)$number; break;
}
$number = '';
}
}

I'd go with the following approach.
$age = '27y5m6w2d';
// Split the string into array of numbers and words
$arr = preg_split('/(?<=[ymdw])/', $age, -1, PREG_SPLIT_NO_EMPTY);
foreach ($arr as $str)
{
$item = substr($str, -1); // Get last character
$value = intval($str); // Get the integer
switch ($item)
{
case 'y':
$year = $value;
break;
case 'm':
$month = $value;
break;
case 'd':
$day = $value;
break;
case 'w':
$week = $value;
break;
}
}
The code is more readable and slightly faster. I tested this with 10000 iterations and it took around just 0.0906 seconds.

You don't need to bloat your code with a lookup array or a switch block.
Your input strings are predictably formatted (in order), so you can write a single regex pattern containing optional capture groups at each of the expected "units" in the input string. While using named capture groups provides some declarative benefit, it also bloats the regex pattern and the output array -- so I generally don't prefer to use them.
You will notice that there is a repeated format in the regex: (?:(\d+)unitLetter)?. This makes modifying/extending the pattern very simple. All of those subpatterns make the targeted substring "optional" and the final letter in the subpattern distinguishes what unit of time is being isolated.
In this case, the match output structure goes:
[0] : the full string match (we don't need it)
[1] : yrs
[2] : mths
[3] : wks
[4] : days
Code: (Demo)
$strings = ['27y5m6w2d', '1m1w', '2w3d', '999y3w', '27d', '5y2d'];
foreach ($strings as $string) {
preg_match('~(?:(\d+)y)?(?:(\d+)m)?(?:(\d+)w)?(?:(\d+)d)?~', $string, $m);
var_export([
'yrs' => $m[1] ?? '',
'mths' => $m[2] ?? '',
'wks' => $m[3] ?? '',
'days' => $m[4] ?? '',
]);
echo "\n---\n";
}
Output:
array (
'yrs' => '27',
'mths' => '5',
'wks' => '6',
'days' => '2',
)
---
array (
'yrs' => '',
'mths' => '1',
'wks' => '1',
'days' => '',
)
---
array (
'yrs' => '',
'mths' => '',
'wks' => '2',
'days' => '3',
)
---
array (
'yrs' => '999',
'mths' => '',
'wks' => '3',
'days' => '',
)
---
array (
'yrs' => '',
'mths' => '',
'wks' => '',
'days' => '27',
)
---
array (
'yrs' => '5',
'mths' => '',
'wks' => '',
'days' => '2',
)
---

Related

Codeigniter controller number to word converter

I am trying to convert any number to words using my Codeigniter Controller.
My Controller as follows :
public function convert_number_to_words($number)
{
$hyphen = ' ';
$conjunction = ' and ';
$separator = ' ';
$negative = 'negative ';
$decimal = ' and Cents ';
$dictionary = array(
0 => 'Zero',
1 => 'One',
2 => 'Two',
3 => 'Three',
4 => 'Four',
5 => 'Five',
6 => 'Six',
7 => 'Seven',
8 => 'Eight',
9 => 'Nine',
10 => 'Ten',
11 => 'Eleven',
12 => 'Twelve',
13 => 'Thirteen',
14 => 'Fourteen',
15 => 'Fifteen',
16 => 'Sixteen',
17 => 'Seventeen',
18 => 'Eighteen',
19 => 'Nineteen',
20 => 'Twenty',
30 => 'Thirty',
40 => 'Fourty',
50 => 'Fifty',
60 => 'Sixty',
70 => 'Seventy',
80 => 'Eighty',
90 => 'Ninety',
100 => 'Hundred',
1000 => 'Thousand',
1000000 => 'Million',
);
if (!is_numeric($number)) {
return false;
}
if ($number < 0) {
return $negative . $this->convert_number_to_words(abs($number));
}
$string = $fraction = null;
if (strpos($number, '.') !== false) {
list($number, $fraction) = explode('.', $number);
}
switch (true) {
case $number < 21:
$string = $dictionary[$number];
break;
case $number < 100:
$tens = ((int)($number / 10)) * 10;
$units = $number % 10;
$string = $dictionary[$tens];
if ($units) {
$string .= $hyphen . $dictionary[$units];
}
break;
case $number < 1000:
$hundreds = $number / 100;
$remainder = $number % 100;
$string = $dictionary[$hundreds] . ' ' . $dictionary[100];
if ($remainder) {
$string .= $conjunction . $this->convert_number_to_words($remainder);
}
break;
default:
$baseUnit = pow(1000, floor(log($number, 1000)));
$numBaseUnits = (int)($number / $baseUnit);
$remainder = $number % $baseUnit;
$string = $this->convert_number_to_words($numBaseUnits) . ' ' . $dictionary[$baseUnit];
if ($remainder) {
$string .= $remainder < 100 ? $conjunction : $separator;
$string .= $this->convert_number_to_words($remainder);
}
break;
}
if (null !== $fraction && is_numeric($fraction)) {
$string .= $decimal;
$words = array();
foreach (str_split((string)$fraction) as $number) {
$words[] = $dictionary[$number];
}
$string .= implode(' ', $words);
}
return $string;
}
public function printWord(){
$numberToWord = $this->Number_model->getNumber();
$this->amount_word = $this->convert_number_to_words($numberToWord ) . " Only ***";
}
The model named "Number_model" returns the number in digits correctly. After that it should be printed using following php code in the view.
<?=$this->amount_word?>
Desired Result
I need to print that number, into words using <?=$this->amount_word?> in my view. But it shows -> Only *** without the converted number in words.
I can not understand what I am going wrong. Can anyone help me ?
As the code in your example works with an hardcoded number and the view returns the concatenated string "Only ***" the problem is most likely that the model's function $this->Number_model->getNumber(); is returning an array. therefore you need to address your function to use a value from this array:
if the array (use print_r($numberToWord) to see how the array looks like) for example is:
Array
(
[0] => 1234
)
we can use:
$this->amount_word = $this->convert_number_to_words($numberToWord[0] )
update from comments:
as the array looks like:
Array
(
[0] => stdClass Object
(
[fuel_qty] => 30
)
)
and want to address fuel_qty, we need to send the array's object value like this:
$this->convert_number_to_words($numberToWord[0]->fuel_qty)
where [0] is the array index and ->fuel_qty the object's key

How to start looping through an array at a different index and finishing the entire array

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

PHP Convert string to array and group by value

Im stuck in solving this, given a string like WWLLWWWLWLLL I want to group it into array so the output should be
[
0 => 'WW',
1 => 'LL',
2 => 'WWW',
3 => 'L',
4 => 'W',
5 => 'LLL',
]
Here is what I have started and I'm stuck what's the next step
$str = 'WWLLWWWLWLLL';
str_split($str);
Bit messy, but it does the job:
$str = 'WWLLWWWLWLLL';
$str = str_split($str);
foreach($str as $k => &$v) {
$c = 1;
while(isset($str[$k + $c]) && $str[$k + $c] == $v[0]) {
$v .= $str[$k + $c];
unset($str[$k + $c]);
$c++;
}
}

Explode only by last delimiter

is there a way to use explode function to explode only by last delimiter occurrence?
$string = "one_two_ ... _three_four";
$explodeResultArray = explode("_", $string);
Result Should Be:
echo $explodeResultArray[0]; // "one_two_three ...";
echo $explodeResultArray[1]; // "four";
Straightforward:
$parts = explode('_', $string);
$last = array_pop($parts);
$parts = array(implode('_', $parts), $last);
echo $parts[0]; // outputs "one_two_three"
Regular expressions:
$parts = preg_split('~_(?=[^_]*$)~', $string);
echo $parts[0]; // outputs "one_two_three"
String reverse:
$reversedParts = explode('_', strrev($string), 2);
echo strrev($reversedParts[0]); // outputs "four"
There is no need for a workaround. explode() accepts a negative limit.
$string = "one_two_three_four";
$part = implode('_', explode('_', $string, -1));
echo $part;
Result is
one_two_three
I chose to use substring becasue you want a string up to a particular point:
$string = "one_two_three_four_five_six_seven";
$part1 = substr("$string",0, strrpos($string,'_'));
$part2 = substr("$string", (strrpos($string,'_') + 1));
var_dump($part1,$part2);
RESULTS:
string(27) "one_two_three_four_five_six"
string(5) "seven"
You could do the following:
$string = "one_two_three_four";
$explode = explode('_', $string); // split all parts
$end = '';
$begin = '';
if(count($explode) > 0){
$end = array_pop($explode); // removes the last element, and returns it
if(count($explode) > 0){
$begin = implode('_', $explode); // glue the remaining pieces back together
}
}
EDIT:
array_shift should have been array_pop
<?php
$lastPos = strrpos($string, '_');
if ($lastPos !== false) {
$start = substr($string, 0, $lastPos);
$end = substr($string, $lastPos+1);
} else {
// no delimeter found!
}
If you only care about the last part, it's even simpler.
<?php
$end = substr(strrchr($string, '_'), 1);
Use preg_match()
$string = "one_two_three_four";
$arr = array();
preg_match("/(^.*)_(.*?)$/", $string, $arr);
print_r($arr);
Output: Array ( [0] => one_two_three_four [1] => one_two_three [2] => four )
use end + explode
$explodeResultArray = end(explode("_", $string));
$explodeResultArray will = four
// reverse $string right after definition
$string = "one_two_three_four_five_six";
$string = implode("_",array_reverse(explode("_",$string)));
// chop off the first part
list($result, $string) = explode("_", $string, 2);
echo "$result --- $string";
Output:
six --- five_four_three_two_one
$explodeResultArray = explode("_", $string);
$last_item = end($explodeResultArray);
$key = count($explodeResultArray) - 1;
unset($explodeResultArray[$key]);
$arr[] = (implode($explodeResultArray,'_'));
$arr[] = $last_item;
print_r($arr);
Output
Array
(
[0] => one_two_ ... _three
[1] => four
)
I had similar needs and inspired by #NLZ's answer I've made a reusable function with the same features as regular explode(), but backwards (although I added an option to reverse the array order contra regular explode()):
function backward_explode($delimiter, $string, $limit = null, $keep_order = true) {
if ((string)$delimiter === "") {
return false;
}
if ($limit === 0 || $limit === 1) {
return array($string);
}
$explode = explode($delimiter, $string);
if ($limit === null || $limit === count($explode)) {
return $keep_order? $explode : array_reverse($explode);
}
$parts = array();
if ($limit > 0) {
for ($i = 1; $i < $limit; $i++) {
$parts[] = array_pop($explode);
}
$remainder = implode($delimiter, $explode);
$parts[] = $remainder;
if ($keep_order) {
$parts = array_reverse($parts);
}
} else {
if (strpos($string, $delimiter) === false) {
return array();
}
$parts = $explode;
array_splice($parts, 0, abs($limit));
if (!$keep_order) {
$parts = array_reverse($parts);
}
}
return $parts;
}
(Also as a gist.)
So with:
$string = 'one two three four';
var_dump(backward_explode(' ', $string));
var_dump(backward_explode(' ', $string, 2));
var_dump(backward_explode(' ', $string, 3));
var_dump(backward_explode(' ', $string, 2, false));
var_dump(backward_explode(' ', $string, -1));
var_dump(backward_explode(' ', $string, 1)); // same as with $limit = 0
var_dump(backward_explode('#', $string, -2));
var_dump(backward_explode('', $string, 3));
We get:
array (size=4)
0 => string 'one' (length=3)
1 => string 'two' (length=3)
2 => string 'three' (length=5)
3 => string 'four' (length=4)
array (size=2)
0 => string 'one two three' (length=13)
1 => string 'four' (length=4)
array (size=3)
0 => string 'one two' (length=7)
1 => string 'three' (length=5)
2 => string 'four' (length=4)
array (size=2)
0 => string 'four' (length=4)
1 => string 'one two three' (length=13)
array (size=3)
0 => string 'two' (length=3)
1 => string 'three' (length=5)
2 => string 'four' (length=4)
array (size=1)
0 => string 'one two three four' (length=18)
array (size=0)
empty
boolean false
For such a taks, you can just use strstr and strrchr:
$valueBeforeLastUnderscore = rtrim(strrev(strstr(strrev($value), '_')), '_');
$valueAfterLastUnderscore = ltrim(strrchr($value, '_'), '_');
That being said, I like the regular expression answer more.
simply use sub string
substr($string ,stripos($string ,'_'))

How to insert element into arrays at specific position?

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.

Categories