class-constrained-array-rounding.php 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. <?php
  2. /**
  3. * Lets you round the numeric elements of an array to integers while preserving their sum.
  4. *
  5. * Usage:
  6. *
  7. * Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $bound_array )
  8. * if a specific sum doesn't need to be specified for the bound array
  9. *
  10. * Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $bound_array, $sum )
  11. * If the sum of $bound_array must equal $sum after rounding.
  12. *
  13. * If $sum is less than the sum of the floor of the elements of the array, the class defaults to using the sum of the array elements.
  14. */
  15. class Jetpack_Constrained_Array_Rounding {
  16. public static function get_rounded_constrained_array( $bound_array, $sum = false ) {
  17. // Convert associative arrays before working with them and convert them back before returning the values
  18. $keys = array_keys( $bound_array );
  19. $bound_array = array_values( $bound_array );
  20. $bound_array_int = self::get_int_floor_array( $bound_array );
  21. $lower_sum = array_sum( wp_list_pluck( $bound_array_int, 'floor' ) );
  22. if ( ! $sum || ( $sum < $lower_sum ) ) {
  23. // If value of sum is not supplied or is invalid, calculate the sum that the returned array is constrained to match
  24. $sum = array_sum( $bound_array );
  25. }
  26. $diff_sum = $sum - $lower_sum;
  27. self::adjust_constrained_array( $bound_array_int, $diff_sum );
  28. $bound_array_fin = wp_list_pluck( $bound_array_int, 'floor' );
  29. return array_combine( $keys, $bound_array_fin );
  30. }
  31. private static function get_int_floor_array( $bound_array ) {
  32. $bound_array_int_floor = array();
  33. foreach ( $bound_array as $i => $value ){
  34. $bound_array_int_floor[$i] = array(
  35. 'floor' => (int) floor( $value ),
  36. 'fraction' => $value - floor( $value ),
  37. 'index' => $i,
  38. );
  39. }
  40. return $bound_array_int_floor;
  41. }
  42. private static function adjust_constrained_array( &$bound_array_int, $adjustment ) {
  43. usort( $bound_array_int, array( 'self', 'cmp_desc_fraction' ) );
  44. $start = 0;
  45. $end = $adjustment - 1;
  46. $length = count( $bound_array_int );
  47. for ( $i = $start; $i <= $end; $i++ ) {
  48. $bound_array_int[ $i % $length ]['floor']++;
  49. }
  50. usort( $bound_array_int, array( 'self', 'cmp_asc_index' ) );
  51. }
  52. private static function cmp_desc_fraction( $a, $b ) {
  53. if ( $a['fraction'] == $b['fraction'] )
  54. return 0;
  55. return $a['fraction'] > $b['fraction'] ? -1 : 1;
  56. }
  57. private static function cmp_asc_index( $a, $b ) {
  58. if ( $a['index'] == $b['index'] )
  59. return 0;
  60. return $a['index'] < $b['index'] ? -1 : 1;
  61. }
  62. }