Arrays PHP

Sorting an array by element

Sorting an array by a particular element can be a bit of a struggle. Sadly enough there isn’t one perfect function to do this, but we can always try to make one.

Sorting an array will always use usort. Usort sends two values from the array (i.e. 1 and 2, 2 and 3, 3 and 4 etc.) to a user-defined function where you compare these two values:

usort($anarray, 'compareFunction');

There are two types of comparison, let’s start with binary safe string comparison (strcmp). This is perfect for strings, but not for integers or floating-point numbers bigger than 9. An array containing [5,33,4,6,9,11,24,3] will return:

Array ( [0] => 11 [1] => 24 [2] => 3 [3] => 33 [4] => 4 [5] => 5 [6] => 6 [7] => 9 )

The only way to fix this is to do a real numerical comparison with integers and floating-point numbers. So we have to split strings and numbers and end up like this:

function strcmpFn($r1, $r2) {
	$key = 'number';
	if(is_numeric($r1[$key]) && is_numeric($r2[$key])){
		return $r1[$key] - $r2[$key];
	}
	return strcmp($r1[$key],$r2[$key]);
}
$rows = array(
			array('number' => 5),
			array('number' => 33),
			array('number' => 4),
			array('number' => 6),
			array('number' => 9),
			array('number' => 11),
			array('number' => 24),
			array('number' => 3)
		);
usort($rows, 'strcmpFn');

print_r($rows);

This returns a nicely sorted array:

Array
(
    [0] => Array
        (
            [number] => 3
        )

    [1] => Array
        (
            [number] => 4
        )

    [2] => Array
        (
            [number] => 5
        )

    [3] => Array
        (
            [number] => 6
        )

    [4] => Array
        (
            [number] => 9
        )

    [5] => Array
        (
            [number] => 11
        )

    [6] => Array
        (
            [number] => 24
        )

    [7] => Array
        (
            [number] => 33
        )

)

You can also sort it descending by changing

	return $r1[$key] - $r2[$key];

to

	return 0 - $r1[$key] - $r2[$key];
Javascript

Valid8: user friendly form validation

Valid8 is a nice script that works with jQuery to validate form fields in a very user friendly way:

It’s very easy to implement and although it’s not maintained any more, the code is easy enough to make adjustments yourself. I’ve made a little change that prevents the errormessage element from being added to the DOM. In my case it used some unnecessary space. Also, I’ve compressed the file a bit more.

jquery.valid8-1.31.js (665) jquery.valid8-1.31.src.js (631)

Coding Math PHP

Linear interpolation with PHP

I’ve always loved working with Excel. Yes, it can be a bit of a struggle, but in the end it always works, right? Lately I’ve been working on a couple of statistical tools, developed in PHP. Before development (okay, sometimes during) development I find it always helpful to put all calculations and formulas in Excel to check if everything that it returns is as expected. Plus this is always a good reference for later use.

One project used the FORECAST-function of Excel, which is basically a linear interpolation and extrapolation calculation. But now, how do you port a statistical function like that to PHP, which is obviously not made for math- and statistics lovers. And sadly enough I’m not that good in math to be able to build the function myself.

After a while I finally found THIS example. It’s a nice example that calculates the slope between two points in an array of X and Y’s. I’ve made some changes to fit my needs:

  • Removed float stuff, which makes more sense
  • Added support for different steps than just 1
  • Added support for Y input and X output (just like Excel)

The output is not the same as Excel’s FORECAST, but in my opinion it’s even better (dataset is on top, 25 is the value we want to interpolate):

Excel LInterpolation

X

Y

X

Y

17

22.6

17

22.6

19

26.6

19

26.6

21

30.3

21

30.3

26

38.9

26

38.9

36

47.3

36

47.3

60

96.9

60

96.9

121

220

121

220

19.746

25

18.2

25

So it’s pretty accurate.

Here’s the code. Maybe not perfect yet, but almost :)

function LInterpolation($arr,$step=1,$returnval) {
	global $smarty;
	
	if(!is_array($arr)){
		$smarty->assign('error', array('str' => '<b>LInterpolation:</b> expects an array'));
		return false;
	}
	
	$narr = array();
	foreach($arr AS $key => $value) {
		$nval = $value;
		$nkey = $key;
		$narr[(string) $nkey] = (string) $nval;
	}
	$arr = $narr;
	
	$array_keys = array_keys($arr);
	$first_key  = current($array_keys);
	$last_key   = end($array_keys);
	
	$arr_calc = array();
	foreach($arr AS $key => $value){
		if(is_numeric($key) == false || is_numeric($value) == false){
			$smarty->assign('error', array('str' => '<b>LInterpolation:</b> array keys & values must be numeric'));
			return false;
		}
		if($key !== $first_key){
			array_push($arr_calc,((($value-$arr["$before_key"]) / ($key-$before_key)) * $step));
		}
		$before_key = $key;
	}
	
   $x = -1;
   $arr_new = array();
   for($i = $first_key; (string) $i <= (string) $last_key; $i=$i+$step){
	  if(!isset($arr["$i"])){
		 $arr_new["$i"] = ($arr_new["$before_key"] + $arr_calc[$x]);
	  } else {
		 $arr_new["$i"] = $arr["$i"];
		 $x++;
	  }
	  if (is_numeric($returnval) && $arr_new["$i"] >= $returnval){
	    if($arr_new["$before_key"] == '' && (float) $returnval !== (float) $arr_new["$i"]){
			$smarty->assign('error', array('str' => 'outofbounds', 'vars' => array($returnval, $i, $arr_new["$i"])));
			return false;
		}
		$diff_key = $i - $before_key;
		$diff_val = $arr_new["$i"] - $arr_new["$before_key"];
		$growth = $returnval - $arr_new["$before_key"];
		$growthperunit = $diff_key / $diff_val;
		$ttlgrowth = $growth * $growthperunit;
		$est = $before_key + $ttlgrowth;
		return $est;
	  }
	  $before_key = $i;
   }
   
   if(is_numeric($returnval)){
		return $before_key;
   }
   return $arr_new;
}

Call the function as followed:

LInterpolation(
   array(
      "17"=>"22.6",
      "19"=>"26.6",
      "21"=>"30.3",
	  "26"=>"38.9",
	  "36"=>"47.3",
	  "60"=>"96.9",
	  "121"=>"220",
   ),1,null)

This will return an array with all predicted values between the first (17) and last element (121). Want to predict a specific X? Use the third element in the function for the Y:

LInterpolation(
   array(
      "17"=>"22.6",
      "19"=>"26.6",
      "21"=>"30.3",
	  "26"=>"38.9",
	  "36"=>"47.3",
	  "60"=>"96.9",
	  "121"=>"220",
   ),1,1.20)

This will return only the exact value of X.