Sort php Array by integers inside values - php

I'm trying to sort an array numerically but the integers are found in the middle of a value, not on the first character. Here is what my array looks like (using dummy values):
$array = Array('A25','A30','A40','B25','B30','B40','AB25','AB30','AB40');
sort($array,1);
Output after foreach:
A25
A30
A40
AB25
AB30
AB40
B25
B30
B40
Expected output:
A25
AB25
B25
A30
AB30
B30
A40
AB40
B40
What would be the best sorting method for this? Really appreciate it, thanks!

$array = Array('A25','A30','A40','B25','B30','B40','AB25','AB30','AB40');
usort(
$array,
function($a, $b) {
list($achar,$anum) = sscanf($a, '%[A-Z]%d');
list($bchar,$bnum) = sscanf($b, '%[A-Z]%d');
if ($anum > $bnum) {
return 1;
} elseif (($anum == $bnum) && ($achar > $bchar)) {
return 1;
}
return -1;
}
);
var_dump($array);

Have created a custom function depending on your requirement, please see below code
<?php
$array = array('A30','A25','ZZZ','A40','Rohan','B25','B30','Sakhale','B40','AB25','AB30','AB40');
$array = sortArrayByNumber($array);
var_dump($array);
/**
* #name sortArrayByNumber
* #abstract sort the entire array irrespective of the string, but the numbers into it
* also we append the elements not having any number at the end
* #author Rohan Sakhale
*/
function sortArrayByNumber($arr){
$sortedArr = array();
$tempArray = array();
$stringOnlyArray = array();
foreach($arr as $v){
$num = getNumberFromString($v);
/**
* If no number found, append it into stringOnlyArray
*/
if($num == ''){
$stringOnlyArray[] = $v;
continue;
}
if(!isset($tempArray[$num])){
$tempArray[$num] = array();
}
$tempArray[$num][] = $v;
}
$tempArrayKeys = array_keys($tempArray);
sort($tempArrayKeys);
foreach($tempArrayKeys as $key){
sort($tempArray[$key]);
$sortedArr = array_merge($sortedArr, $tempArray[$key]);
}
if(count($stringOnlyArray) > 0){
sort($stringOnlyArray);
$sortedArr = array_merge($sortedArr, $stringOnlyArray);
}
return $sortedArr;
}
/**
* #str - String param which tend to have number
*/
function getNumberFromString($str){
$matches = null;
preg_match_all('!\d+!', $str, $matches);
if(!is_null($matches) and is_array($matches)){
if(isset($matches[0][0])){
return $matches[0][0];
}
}
return '';
}
?>
Here sort by number method is trying to firstly identify the number's in your string and maintain a separate array of it based on the keys, later we sort in ascending order every key's of number and then we finally merge it into sorted array

You can use user defined function with usort to achieve it
<?php
function mySort($a,$b) {
$numA = '';
$strA = '';
$numB = '';
$strB = '';
for($i=0;$i<strlen($a); $i++) {
if(is_numeric($a[$i])) {
$numA .= (string)$a[$i];
}
else {
$strA .= $a[$i];
}
}
for($i=0;$i<strlen($b); $i++) {
if(is_numeric($b[$i])) {
$numB .= (string)$b[$i];
}
else {
$strB .= $b[$i];
}
}
$numA = (int)$numA;
$numB = (int)$numB;
if($numA>$numB) {
return true;
}
elseif($numA<$numB) {
return false;
}
else {
if(strcmp($strA,$strB)>0) {
return true;
}
else {
return false;
}
}
}
$array = Array('A25','A30','A40','B25','B30','B40','AB25','AB30','AB40');
var_dump($array);
usort($array,'mySort');
var_dump($array);
?>

Here's a stable sort, the result is: "A25" "B25" "AB25" "A30" "B30" "AB30" "A40" "B40" "AB40" .
function getNum($str)
{
preg_match ( '/(\d+)$/', $str, $match );
return intval ( $match [1] );
}
function stableSort($arr)
{
$newArr = array ();
foreach ( $arr as $idx => $ele )
{
$newArr [] = array (
'idx' => $idx,
'val' => $ele
);
}
usort ( $newArr,
function ($a, $b)
{
$d = getNum ( $a ['val'] ) - getNum ( $b ['val'] );
return $d ? $d : $a ['idx'] - $b ['idx'];
} );
$sortArr = array ();
foreach ( $newArr as $ele )
{
$sortArr [] = $ele ['val'];
}
return $sortArr;
}
var_dump ( stableSort ( $array ) );

U can use various kinds of ways to sort arrays in PHP. PHP has some good ways build in to do sorting on arrays. In this case I agree with Mark Baker, however I would recommend a preg_replace to get the numeric value of your strings.
$array = Array('A25','A30','A40','B25','B30','B40','AB25','AB30','AB40');
function cmp($a, $b)
{
$v1 = preg_replace("/[^0-9]/", "", $a);
$v2 = preg_replace("/[^0-9]/", "", $b);
if ($v1 == $v2) {
return 0;
}
return ($v1 < $v2) ? -1 : 1;
}
usort($array, "cmp");
var_dump($array);
Look into the PHP information about sorting arrays, php.net/usort and various others.

Related

How to recursively combine array in php

I want to combine two arrays into a dictionary.
The keys will be the distinct values of the first array, the values will be all values from the second array, at matching index positions of the key.
<?php
$a=[2,3,4,5,6,7,8,9,10];
$b=[1,1,3,2,1,2,6,8,8];
?>
array_combine($b,$a);
Expected result as
<?php
/*
Value '1' occurs at index 0, 1 and 4 in $b
Those indices map to values 2, 3 and 6 in $a
*/
$result=[1=>[2,3,6],3=>4,2=>[5,7],6=>8,8=>[9,10]];
?>
There are quite a few PHP array functions. I'm not aware of one that solves your specific problem. you might be able to use some combination of built in php array functions but it might take you a while to weed through your choices and put them together in the correct way. I would just write my own function.
Something like this:
function myCustomArrayFormatter($array1, $array2) {
$result = array();
$num_occurrences = array_count_values($array1);
foreach ($array1 AS $key => $var) {
if ($num_occurrences[$var] > 1) {
$result[$var][] = $array2[$key];
} else {
$result[$var] = $array2[$key];
}
}
return $result;
}
hope that helps.
$a=[2,3,4,5,6,7,8,9,10];
$b=[1,1,3,2,1,2,6,8,8];
$results = array();
for ($x = 0; $x < count($b); $x++) {
$index = $b[$x];
if(array_key_exists ($index, $results)){
$temp = $results[$index];
}else{
$temp = array();
}
$temp[] = $a[$x];
$results[$index] = $temp;
}
print_r($results);
Here's one way to do this:
$res = [];
foreach ($b as $b_index => $b_val) {
if (!empty($res[$b_val])) {
if (is_array($res[$b_val])) {
$res[$b_val][] = $a[$b_index];
} else {
$res[$b_val] = [$res[$b_val], $a[$b_index]];
}
} else {
$res[$b_val] = $a[$b_index];
}
}
var_dump($res);
UPDATE: another way to do this:
$val_to_index = array_combine($a, $b);
$result = [];
foreach ($val_to_index as $value => $index) {
if(empty($result[$index])){
$result[$index] = $value;
} else if(is_array($result[$index])){
$result[$index][] = $value;
} else {
$result[$index] = [$result[$index], $value];
}
}
var_dump($result);

How can I find not just duplicates in array, but numbers that match a range in PHP?

I need to find not just duplicates in an array, but numbers that are within 1 of each other.
example: $myArr = array(46,78,77,43,86,1,47,14,51,31)
How would I get find those numbers if there no duplicates were found?
Thanks.
This will work - tested:
$myArr = array(46,78,77,43,86,1,47,14,51,31);
$i = 1;
while($i <= 99){
if(in_array($i, $myArr)){
// This means it's in the array
// Let's see if it's +1 of the last #
if(isset($last_num)){
$res = ($i + 1);
if(in_array($res, $myArr)){
echo $res . ' is in the array<br>';
}
}
}
$last_num = $i;
$i++;
}
Could use something like this.
<?
$haystack = array(31,46,78,77,43,86,1,47,14,51,31);
array_walk($haystack, function($key, $value) use ($haystack) {
$needle = array($key - 1, $key, $key + 1);
$r = array_intersect($haystack, $needle);
if(count($r) > 1) { unset($r[$value]); print_r(array_values($r)); }
});
?>
Or if you want them returned in an array:
<?
$haystack = array(31,46,78,77,43,86,1,47,14,51,31);
$result = array_filter(array_map(function($key, $value) use ($haystack) {
$needle = array($key - 1, $key, $key + 1);
$r = array_intersect($haystack, $needle);
if(count($r) > 1) { unset($r[$value]); return array_values($r)['0']; }
}, $haystack));
print_r($result);
?>
A little more verbose, but it may be more efficient since we're only looping through the array when necessary. It will return the keys of the original array where duplicates and values within range are found. (Note: I've expanded the code out a lot more than I normally would just for readability for OP)
/**
* Find the any values within an array that are within
* the specified range from another value.
*
* #param Array Array to search in
* #param uint Unsigned integer used for comparison
* #return Array The keys of all found values
**/
function findWithinRange(Array $array, $range) {
// sort the array values numerically
// so we don't have to loop through
// the entire array for each check
asort($array, SORT_NUMERIC);
// Keep the keys in case it's an associative array
$keys = array_keys($array);
// Get the values without the keys so we can easily loop
$values = array_values($array);
$return = array();
// Loop over each item in the array
foreach( $values as $k => $v ) {
$start = $k;
$min = $v - $range;
$max = $v + $range;
// track backwards in the array until we're less than range
// We could use array_search, but we'd be calling it multiple times
// This only runs through the array once and dies the moment
// it is out of the specified range
while(true) {
$k--;
if( !isset($values[$k]) ) {
break; // don't continue if we run out of keys
}
$curVal = $values[$k];
if( $curVal >= $min ) {
$return[] = $keys[$k];
}
else {
break; // kill the while loop if we're outside of range
}
}
// reset
$k = $start;
// track forward in the array until we're greater than range
while(true) {
$k++;
if( !isset($values[$k]) ) {
break; // don't continue if we run out of keys
}
$curVal = $values[$k];
if( $curVal <= $max ) {
$return[] = $keys[$k];
}
else {
break; // kill the while loop if we're outside of range
}
}
}
// drop duplicate reports
$return = array_unique($return);
// return all found keys
return $return;
}
Example usage:
$myArr = array(46,78,77,43,86,1,47,14,51,31);
$res = findWithinRange($myArr, 1);
var_export($res);
// array(6, 0, 1, 2)

Merge every other array php

array one: 1,3,5,7
array two: 2,4,6,8
the array i want would be 1,2,3,4,5,6,7,8
I'm just using numbers as examples. If it was just numbers i could merge and sort but they will be words. So maybe something like
array one: bob,a,awesome
array two: is,really,dude
should read: bob is a really awesome dude
Not really sure how to do this. Does PHP have something like this built in?
You could write yourself a function like this:
function array_merge_alternating($array1, $array2) {
if(count($array1) != count($array2)) {
return false; // Arrays must be the same length
}
$mergedArray = array();
while(count($array1) > 0) {
$mergedArray[] = array_shift($array1);
$mergedArray[] = array_shift($array2);
}
return $mergedArray;
}
This function expects two arrays with equal length and merges their values.
If you don't need your values in alternating order you can use array_merge. array_merge will append the second array to the first and will not do what you ask.
Try this elegant solution
function array_alternate($array1, $array2)
{
$result = Array();
array_map(function($item1, $item2) use (&$result)
{
$result[] = $item1;
$result[] = $item2;
}, $array1, $array2);
return $result;
}
This solution works AND it doesn't matter if both arrays are different sizes/lengths:
function array_merge_alternating($array1, $array2)
{
$mergedArray = array();
while( count($array1) > 0 || count($array2) > 0 )
{
if ( count($array1) > 0 )
$mergedArray[] = array_shift($array1);
if ( count($array2) > 0 )
$mergedArray[] = array_shift($array2);
}
return $mergedArray;
}
Try this function:
function arrayMergeX()
{
$arrays = func_get_args();
$arrayCount = count($arrays);
if ( $arrayCount < 0 )
throw new ErrorException('No arguments passed!');
$resArr = array();
$maxLength = count($arrays[0]);
for ( $i=0; $i<$maxLength; $i+=($arrayCount-1) )
{
for ($j=0; $j<$arrayCount; $j++)
{
$resArr[] = $arrays[$j][$i];
}
}
return $resArr;
}
var_dump( arrayMergeX(array(1,3,5,7), array(2,4,6,8)) );
var_dump( arrayMergeX(array('You', 'very'), array('are', 'intelligent.')) );
var_dump( arrayMergeX() );
It works with variable numbers of arrays!
Live on codepad.org: http://codepad.org/c6ZuldEO
if arrays contains numeric values only, you can use merge and sort the array.
<?php
$a = array(1,3,5,7);
$b = array(2,4,6,8);
$merged_array = array_merge($a,$b);
sort($merged,SORT_ASC);
?>
else use this solution.
<?php
function my_merge($array1,$array2)
{
$newarray = array();
foreach($array1 as $key => $val)
{
$newarray[] = $val;
if(count($array2) > 0)
$newarray[] = array_shift($array2)
}
return $newarray;
}
?>
hope this help
Expects both arrays to have the same length:
$result = array();
foreach ($array1 as $i => $elem) {
array_push($result, $elem, $array2[$i]);
}
echo join(' ', $result);

permutation/generate combination prefix and suffix

I have an array of prefixes, an array of base words and an array of suffixes. I would like to see every combination that can be made.
Example:
prefixes: 1 2
words: hello test
suffixes: _x _y
Results:
1hello_x
1hello_y
1hello
1test_x
1test_y
1test
1_x
1_y
1
2hello_x
2hello_y
2hello
2test_x
2test_y
2test
2_x
2_y
2
hello_x
hello_y
hello
test_x
test_y
test
_x
_y
y
How can I do this?
Edit: Thanks for all the responses, I am going through the solutions but it seems like if there are no prefixes, then it will fail for combination. It should still go through the base words and suffixes, even without any prefixes.
function combineAll ($prefixes, $words, $suffixes)
{
$combinations = array ();
foreach ($prefixes as $prefix)
{
foreach ($words as $word)
{
foreach ($suffixes as $suffix)
{
$combinations[] = $prefix.$word.$suffix;
}
}
}
return $combinations;
}
for each $prefix in $prefixes {
for each $base in $basewords {
for each $suffix in $suffixes {
echo $prefix.$base.$suffix."\n"
}}}
This will do what you want, I believe there is no builtin function to do this in php (Though there is in python)
this should get you started:
http://ask.amoeba.co.in/php-combinations-of-array-elements/
//$a = array("1", "2");
$b = array("hello", "test");
$c = array("_x", "_y");
if(is_array($a)){
$aG = array($a,$b, $c);
}else{
$aG = array($b, $c);
}
$codes = array();
$pos = 0;
generateCodes($aG);
function generateCodes($arr) {
global $codes, $pos;
if(count($arr)) {
for($i=0; $i<count($arr[0]); $i++) {
$tmp = $arr;
$codes[$pos] = $arr[0][$i];
$tarr = array_shift($tmp);
$pos++;
generateCodes($tmp);
}
} else {
echo join("", $codes)."<br/>";
}
$pos--;
}
result:
1hello_x1hello_y1test_x1test_y2hello_x2hello_y2test_x2test_y

how to write function Max($array) which returns the maximum value contained in $array or some array nested within $array

Eg:
$array= array(array(141,151,161),2,3,array(101,202,array(303,606)));
output :606
What you need is to recursively go through your array ; which means the max function, which is not recursive, will not be "enough".
But, if you take a look at the users's notes on the manual page of max, you'll find this note from tim, who proposes this recursive function (quoting) :
function multimax( $array ) {
// use foreach to iterate over our input array.
foreach( $array as $value ) {
// check if $value is an array...
if( is_array($value) ) {
// ... $value is an array so recursively pass it into multimax() to
// determine it's highest value.
$subvalue = multimax($value);
// if the returned $subvalue is greater than our current highest value,
// set it as our $return value.
if( $subvalue > $return ) {
$return = $subvalue;
}
} elseif($value > $return) {
// ... $value is not an array so set the return variable if it's greater
// than our highest value so far.
$return = $value;
}
}
// return (what should be) the highest value from any dimension.
return $return;
}
Using it on your array :
$arr= array(array(141,151,161),2,3,array(101,202,array(303,404)));
$max = multimax($arr);
var_dump($max);
Gives :
int 404
Of course, this will require a bit more testing -- but it should at least be a start.
(Going through the users' notes on manual pages is always a good idea : if you're having a problem, chances are someone else has already had that problem ;-) )
Same idea as Pascal's solution, only shorter thanks to the Standard PHP Library
$arr= array(array(141,151,161),2,3,array(101,202,array(303,404)));
echo rmax($arr);
function rmax(array $arr) {
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));
// initialize $max
$it->next(); $max = $it->current();
// "iterate" over all values
foreach($it as $v) {
if ( $v > $max ) {
$max = $v;
}
}
return $max;
}
http://www.java2s.com/Code/Php/Data-Structure/FindtheMaximumValueinaMultidimensionalArray.htm
function recursive_array_max($a) {
foreach ($a as $value) {
if (is_array($value)) {
$value = recursive_array_max($value);
}
if (!(isset($max))) {
$max = $value;
} else {
$max = $value > $max ? $value : $max;
}
}
return $max;
}
$dimensional = array(
7,
array(3, 5),
array(5, 4, 7, array(3, 4, 6), 6),
14,
2,
array(5, 4, 3)
);
$max = recursive_array_max($dimensional);
$arr = array(array(141,151,161), 2, 3, array(101, 202, array(303,404)));
$callback = function ($value, $key) use (&$maximo) {
if( $value > $maximo){
$maximo = $value;
}
};
array_walk_recursive($arr, $callback);
echo '<pre>'; print_r($maximo);``
A more elegant solution:
function MaxArray($arr)
{
function findMax($currentValue, $currentKey)
{
global $maxValue;
$maxValue = ($currentValue > $maxValue ? $currentValue : $maxValue);
}
array_walk_recursive($arr, 'findMax');
return $GLOBALS['maxValue'];
}
It's very simple
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));
$max = max(iterator_to_array($iterator, false));
Finding the max avoiding too much recursion :
function array_max(array $array) {
$context = func_get_args();
$max = -INF;
while (!empty($context)) {
$array = array_pop($context);
while (!empty($array)) {
$value = array_pop($array);
if (is_array($value)) {
array_push($context, $value);
}
elseif ($max < $value) {
$max = $value;
}
}
}
return $max;
}
A more general method avoiding too much recursion :
function array_reduce_recursive($default, array $array, $callback = null) {
$context = func_get_args();
$count = func_num_args();
if (is_callable(func_get_arg($count - 1))) {
$callback = array_pop($context);
}
else {
$callback = create_function('$x, $y', 'return $x < $y ? $y : $x;');
}
$reduced = array_shift($context);
while (!empty($context)) {
$array = array_pop($context);
while (!empty($array)) {
$value = array_pop($array);
if (is_array($value)) {
array_push($context, $value);
}
else {
$reduced = $callback($reduced, $value);
}
}
}
return $reduced;
}
function array_max_recursive() {
$args = func_get_args();
$callback = create_function('$x, $y', 'return $x < $y ? $y : $x;');
return array_reduce_recursive(-INF, $args, $callback);
}
By this way you can specify the callback method if you are looking for something else than the biggest number. Also this method takes several arrays.
The end way is less efficient of course.
With this you have a full compatibility with lot of PHP version.
function MaxArray($arr) {
$maximum = 0;
foreach ($arr as $value) {
if (is_array($value)) {
//print_r($value);
$tmaximum = MaxArray($value);
if($tmaximum > $maximum){
$maximum = $tmaximum;
}
}
else
{
//echo $value.'\n';
if($value > $maximum){
$maximum = $value;
}
}
}
return $maximum;
}

Categories