This question already has answers here:
Populate array of integers from a comma-separated string of numbers and hyphenated number ranges
(8 answers)
Closed 6 months ago.
I have a text box that a user can paste a string of comma separated numbers and it puts that into an array. However, in some cases that string may contain numbers separated by a dash and I need to fill in the missing numbers where that dash appears.
Example:
1, 4, 7, 20-25, 31, 46, 100
Needs to become:
1, 4, 7, 20, 21, 22, 23, 24, 25, 31, 46, 100
How might I go about doing this? The gap wont always be 5 numbers and there could be more than one set of dashed numbers in the string that is input.
Here is one way, without a regex:
It is:
Parsing the input string with str_getcsv() to separate it into its individual elements, then trimming whitespace around each element with trim() and array_map().
Iterating over each element, searching for a dash. If it finds a dash, it found a range of numbers, so it separates them into the start and end indexes by explode()ing on the dash, and forms the correct range between $start and $end with range(). It then merges that newly formed range with what has already been formed as the result using array_merge().
If, during iteration, it didn't find a dash, it assumes you have a literal number, and adds just that number to the resulting array.
That's it!
$input = '1, 4, 7, 20-25, 31, 46, 100';
$entries = array_map( 'trim', str_getcsv( $input));
$result = array();
foreach( $entries as $entry) {
if( strpos( $entry, '-') !== false) {
list( $start, $end) = explode( '-', $entry);
$result = array_merge( $result, range( trim($start), trim($end)));
} else {
$result[] = $entry;
}
}
You can see from this demo that it produces:
Array (
[0] => 1
[1] => 4
[2] => 7
[3] => 20
[4] => 21
[5] => 22
[6] => 23
[7] => 24
[8] => 25
[9] => 31
[10] => 46
[11] => 100 )
Edit: To form the resulting string, instead of the array, you just need a call to implode(), like so:
echo implode( ', ', $result);
This will produce:
1, 4, 7, 20, 21, 22, 23, 24, 25, 31, 46, 100
$data = '1, 4, 7, 20-25, 31, 46, 100';
$elts = explode(',', $data);
$res = array();
foreach ($elts as $elt) {
$elt = trim($elt);
if (preg_match('/(\d+)-(\d+)/', $elt, $matches) === 1) {
$res = array_merge($res, range($matches[1], $matches[2]));
} else {
$res[] = intval($elt);
}
}
var_dump($res);
My solution :DEMO HERE
First delete spaces using str_replace()
explode() string using , to array of integers.
explode() every element using - to check if it's a range or just a single integer.
if it's a range , loop using for() or range() and add $i to $newArray , else add the element to $newArray.
implode() using , and print the result.
PHP code :
<?php
$str=str_replace(' ', '', '1, 4, 7, 20-25, 31, 46, 100');
$arr = explode(',',$str);
foreach($arr as $elem){
$values=explode('-',$elem);
if(count($values)!=1) for($i=$values[0];$i<=$values[1];$i++) $newArr[]=$i;
else $newArr[]=$elem;
}
echo implode(',',$newArr);
?>
OUTPUT:
1,4,7,20,21,22,23,24,25,31,46,100
Simple code :
split string by delimiter , with explode();
remove space from the beginning and end of a string with trim;
function array_map is used so you don't need to do foreach loop just to apply trim() to every element;
create empty array where you will store new elements;
iterate over each number or range with foreach;
do points 1., 2. and 3. again on every iteration;
split string by delimiter - with explode() even if string doesn't have delimiter - in it; result will be array with one or multi elements;
if point 6. has array with multiple elements, that means that there is range involved, so use function range() to create an array containing a range of elements;
store new numbers to point 4. array with array_merge() function;
when point 4. array is full generated; sort values with sort(), so you don't have output like 5, 3, 1, 4 etc. (delete this function if you don't need this functionality);
remove duplicates with array_unique(), so you don't have output like 4, 4, 4, 4, 5 etc. (delete this function if you don't need this functionality)
to convert array back to string like you inputed, use implode() function.
|
function fillGaps($s) {
$s = array_map('trim', explode(',', $s));
$ss = [];
foreach ($s as $n) {
$n = array_map('trim', explode('-', $n));
if (count($n) > 1) $n = range($n[0], end($n));
$ss = array_merge($ss, $n);
}
sort($ss); # remove if you don't need sorting
$ss = array_unique($ss); # remove if duplicates are allowed
return implode(', ', $ss);
}
Example :
Your example :
echo fillGaps('1, 4, 7, 20 - 25, 31, 46, 100');
# 1, 4, 7, 20, 21, 22, 23, 24, 25, 31, 46, 100
This code will also work for multiple gaps, like :
echo fillGaps('100-105-110');
# 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110
You can even go reverse :
echo fillGaps('10-5');
# 5, 6, 7, 8, 9, 10
# or remove function sort() and output will be 10, 9, 8, 7, 6, 5
Remove duplicates :
echo fillGaps('2-4, 4, 4, 5');
# 2, 3, 4, 5
This can be done without the using a regular expression.
Get the user input, explode with , as the delimiter, and you'll get an array of all numbers.
Now use an if statement to check if the string contains a dash.
If it does, explode it again with the delimiter -. Now you'll get two numbers
Use range() to create an array of numbers within that range
Use implode() to join them by a comma.
As a useful function:
Code:
function fill_string_gaps($input)
{
$parts = explode(',', $input);
$resultArray = array();
foreach ($parts as $part) {
if(strpos(trim($part), '-')) {
list($num1, $num2) = explode('-', $part);
$expanded_num_array = range($num1, $num2);
$resultArray = array_merge($resultArray, $expanded_num_array);
} else {
$resultArray[] = trim($part);
}
}
$comma_separated = implode(', ', $resultArray);
return $comma_separated;
}
Usage:
$input = '1, 4, 7, 20-25, 31, 46, 100';
echo fill_string_gaps($input;)
Test cases:
echo fill_string_gaps('1-5');
echo fill_string_gaps('1-5, 12, 24');
echo fill_string_gaps('2, 2, 4, 5-8');
Output:
1, 2, 3, 4, 5
1, 2, 3, 4, 5, 12, 24
2, 2, 4, 5, 6, 7, 8
See it in action!
Related
I have this array and I need to use it in Charts
in data index I have this value [1,9] and it's coming form the comma split explode function without any quotes around it.
$main_arr = array(
"label" => 'Total Clicks',
"data" => [$total_clicks],
"backgroundColor" => "rgba(255, 0, 0, 1)",
);
Then I use json_encode to turn the array into json format,
[{"label":"Total Clicks","data":["1, 9"],"backgroundColor":"rgba(255, 0, 0, 1)"}]
As you can see above there are double quotes in the square bracket, if I pass static value in the data index i.e [1, 9] it works fine. I tried regex, substring, rtrim etc but didn't work anyone.
Your help would be much appreciated!
You have several problems at once here. First of all your values are strings, and secondly you have an multiple values that you want to explode so you have singular values:
$total_clicks = '1, 9'; // value guessed based on unexpected output in question
$clickArray = explode(',', $total_clicks);
$clickArray = array_map('trim', $clickArray); // remove white spaces
$clickArray = array_map('intval', $clickArray); // cast everything to int
$main_arr = array(
"label" => 'Total Clicks',
"data" => $clickArray,
"backgroundColor" => "rgba(255, 0, 0, 1)",
);
echo json_encode($main_arr);
this outputs:
{"label":"Total Clicks","data":[1,9],"backgroundColor":"rgba(255, 0, 0, 1)"}
For a more sloppy approach you could even skip the line where I trim the whitespaces away, as casting to integer will do this implicitly, however I like to have a clean flow of handled data.
Converting string to array of ints:
$total_clicks = "1, 9";
print_r(array_map('intval', explode(', ', $total_clicks)));
Converting string to array of strings:
$total_clicks = "1, 9";
print_r(array_map('trim', explode(', ', $total_clicks)));
I have around five arrays, where one of them is containing dates. I would like to sort these after the array containing dates. I know how to sort the date array using "usort". I also know how to sort multiple arrays using "array_multisort", but do not know how to combine these. Is there a way doing this? I hope you can help, and tell me if you need more information to solve my problem :)
Edit:
Here are my arrays:
$fid=array(1, 2, 3, 4, 5, 6, 7, 8);
$ftitle=array("Title1", "Title2", "Title3", "Title4", "Title1", "Title2", "Title3", "Title4");
$fbeskrivelse=array("Beskrivelse1", "Beskrivelse2", "Beskrivelse3", "Beskrivelse4", "Beskrivelse1", "Beskrivelse2", "Beskrivelse3", "Beskrivelse4");
$fstøtter=array(2, 15, 7, 10, 3, 4, 5, 6);
$fstartdato=array('11-01-2012', '01-01-2014', '01-01-2015', '09-02-2013', '01-01-2013', '11-01-2017', '01-01-2018', '01-01-2019');
So this is the kind of result I want (after the sorting):
$fid=array(1, 3, 4, 2, 5, 6, 7, 8);
$ftitle=array("Title1", "Title3", "Title4", "Title2", "Title1", "Title2", "Title3", "Title4");
$fbeskrivelse=array("Beskrivelse1", "Beskrivelse3", "Beskrivelse4", "Beskrivelse2", "Beskrivelse1", "Beskrivelse2", "Beskrivelse3", "Beskrivelse4");
$fstøtter=array(2, 7, 10, 15, 3, 4, 5, 6);
$fstartdato=array('11-01-2012', '01-01-2013', '09-02-2013', '01-01-2014', '01-01-2015', '11-01-2017', '01-01-2018', '01-01-2019');
If I'm understanding correctly, you have (lets say 3) arrays that you want to sort, one of which contains dates, and you want to sort by the dates, and since multisort does not support a callback or sort on dates, you aren't sure what to do?
e.g.
$arr1 = array('2019-05-15', '2019-05-17', '2019-05-13')
$arr2 = array('Wed','Fri','Mon');
$arr3 = array('Pork','Steak','Chicken');
If it were me, I'd probably just parse the dates into unix_time in a new array and use that new array as my sorting "key" to sort the rest, since multisort can do numbers.
(Not tested, just my theory)
$key = array_map('strtotime', $arr1);
array_multisort($key, SORT_ASC, SORT_NUMERIC, $arr1, $arr2, $arr3);
Am I overlooking a function in PHP that will merge arrays without preserving keys? I've tried both ways of these ways: $arrayA + $arrayB and array_merge($arrayA, $arrayB) but neither are working as I expected.
What I expect is that when I add array(11, 12, 13) and array(1, 2, 3) together that I would end up with array(11, 12, 13, 1, 2, 3).
I created a function of my own which handles it properly, though I was trying to figure out if it was the best way of doing things or if there's an easier or even a build in way that I'm just not seeing:
function array_join($arrayA, $arrayB) {
foreach($arrayB as $B) $arrayA[] = $B;
return $arrayA;
}
Edit:
array_merge() was working as intended, however I had the function running in a loop and I was using the incorrect variable name inside the function, so it was only returning a partial list as is what I was experiencing. For example the loop I was using ended on array(13, 1, 2, 3).
Have you actually tested your code? because array_merge should be enough:
From the documentation of array_merge:
Merges the elements of one or more arrays together so that the values of one are appended to the end of the previous one. (emphasis are mine)
<?php
$a1 = array(11, 12, 13);
$a2 = array(1, 2, 3);
$x = array_merge($a1, $a2);
print_r($x);
It print this on my console:
Array
(
[0] => 11
[1] => 12
[2] => 13
[3] => 1
[4] => 2
[5] => 3
)
$arr1 = array(11, 12, 13);
$arr2 = array(1, 2, 3);
print_r(array_merge($arr1,$arr2));
Try this :
function array_join($arrayA, $arrayB) {
foreach($arrayB as $B) $arrayA[count($arrayA)] = $B;
return $arrayA;
}
I need to count the number of times a value occurs in a given array.
For example:
$array = array(5, 5, 2, 1);
// 5 = 2 times
// 2 = 1 time
// 1 = 1 time
Does such a function exist? If so, please point me to it in the php docs... because I can't seem to find it.
Thanks.
Yes, it's called array_count_values().
$array = array(5, 5, 2, 1);
$counts = array_count_values($array); // Array(5 => 2, 2 => 1, 1 => 1)
array_count_values
In PHP, say that you have an associative array like this:
$pets = array(
"cats" => 1,
"dogs" => 2,
"fish" => 3
);
How would I find the key with the lowest value? Here, I'd be looking for cats.
Is there some built in PHP function that I've missed which does this? It would also be great if there was a solution that accounted for several values being identical, as below:
$pets = array(
"cats" => 1,
"dogs" => 1,
"fish" => 2
);
Above, I wouldn't mind if it just output either; cats or dogs.
Thanks in advance.
array_keys is your friend:
$pets = array(
"cats" => 1,
"dogs" => 2,
"fish" => 3
);
array_keys($pets, min($pets)); # array('cats')
P.S.: there is a dup here somewhere on SO (it had max instead of min, but I can distinctly remember it).
Thats how i did it.
$pets = array(
"cats" => 1,
"dogs" => 2,
"fish" => 3
);
array_search(min($pets), $pets);
I hope that helps
Might try looking into these:
natcasesort(array)
natsort(array)
$min_val = null;
$min_key = null;
foreach($pets as $pet => $val) {
if ($val < $min_val) {
$min_val = $min;
$min_key = $key;
}
}
You can also flip the array and sort it by key:
$flipped = array_flip($pets);
ksort($flipped);
Then the first key is the minimum, and its value is the key in the original array.
Another approach for retrieving a single string is by using a desirable sorting method and retrieving the first key directly by using key() on the sorted array. In this instance the key with the lowest value is desired, asort will sort from lowest to highest values and reset the internal pointer. To retrieve the reverse (highest to lowest) use arsort.
Example: https://3v4l.org/5ijPh
$pets = array(
"dogs" => 2,
"cats" => 1,
"fish" => 3
);
asort($pets);
var_dump(key($pets));
//string(4) "cats"
$pets = array(
"dogs" => 1,
"cats" => 1,
"fish" => 3
);
asort($pets);
var_dump(key($pets));
//string(4) "dogs"
Take note that all of the PHP array sorting methods will alter the array by-reference.
To prevent altering the original array, create a copy of the array or use an Iterator.
$petsSorted = $pets;
asort($petsSorted);
key($petsSorted);
find the highest value
print max(120, 7, 8, 50);
returns --> 120
$array = array(100, 7, 8, 50, 155, 78);
print max($array);
returns --> 155
find the lowest value
print min(120, 7, 8, 50);
returns --> 7
$array = array(50, 7, 8, 101, 5, 78);
print min($array);
returns --> 5