40

I have an array like this

$data = array(
    "163",
    "630",
    "43",
    "924",
    "4",
    "54"
);

How can I select the smallest and largest values from it according to string length NOT number value. (for this example it is 1 (smallest) and 3 (largest).

2
  • 2
    Do you just want the string length or would you want the strings as well? Commented Sep 14, 2010 at 23:04
  • You should have removed the length/value confusion by using strings instead of these numbers. Commented Dec 15, 2017 at 18:29

6 Answers 6

66

Seems like you should use an array_map()

  // Convert array to an array of string lengths
$lengths = array_map('strlen', $data);

  // Show min and max string length
echo "The shortest is " . min($lengths) .
     ". The longest is " . max($lengths);

Note that the $lengths array is unsorted, so you can easily retrieve the corresponding number for each string length.

Sign up to request clarification or add additional context in comments.

4 Comments

+1 Neat. You could just do array_map('strlen', $data) though;
This is by far the cleanest solution, but it's not very efficient, as it traverses the array three times (array_map, min and max), so if you have huge arrays, better use a single loop.
@Vinko - Yeah, I guess that's where NullUserException's answer would gain the advantage (in the case of a huge array... or using this function very very many times on many many arrays).
Be careful of strlen - it is string length in bytes not count of chars. Count of chars is bigger in case of special (multibyte) chars so mb_strlen must be used.
5

Here's an improved version of brian_d's code:

$min = PHP_INT_MAX;
$max = -1;

foreach ($data as $a) {
    $length = strlen($a);
    $max = max($max, $length);
    $min = min($min, $length);
}

2 Comments

This seems overly complex (with the addition of an initialized $min and $max), unless you are dealing with a huge array that you only want to traverse once.
Yeesh, three function calls on every iteration? I wouldn't.
4

Although in this case it is not advisable because you'll be traversing the array twice, you can also use array_reduce to compare each element against the rest. Like this:

<?php

$data = array('163','630','43','42','999','31');
//Will return the longest element that is nearest to the end of the array (999)
//That's why we use strlen() on the result.
$max_l = strlen(array_reduce($data,'maxlen'));
//Will return the shortest element that is nearest to the end of the array (31)
$min_l = strlen(array_reduce($data,'minlen'));

echo "The longest word is $max_l characters, while the shortest is $min_l\n";

function maxlen($k,$v) {
        if (strlen($k) > strlen($v)) return $k;
        return $v;
}
function minlen($k,$v) {
        if ($k == '') return PHP_INT_MAX;
        if (strlen($k) < strlen($v)) return $k;
        return $v;
}
?>

If you are using PHP 5.3.0+ you can take advantage of closures:

<?php
   $max_l = strlen(array_reduce($data,
                function ($k,$v) { return (strlen($k) > strlen($v)) ? $k : $v; }
        ));

   $min_l = strlen(array_reduce($data,
                function ($k,$v) {
                        if (!$k) return PHP_INT_MAX;
                        return (strlen($k) < strlen($v)) ? $k : $v;
                }
        ));

echo "The longest word is $max_l characters, while the shortest is $min_l\n";
?>

Comments

2
$min = 100;
$max = -1;

foreach($data as $a){
  $length = strlen($a);
  if($length > $max){ $max = $length; }
  else if($length < $min){ $min = $length; }
}

5 Comments

it does exactly what the op wants.
that's right though. $min has to start at a very high number (int32.maxvalue perhaps?)
Or you can set $max/$min to the first element and (optionally) skip it in the iteration. (Easier to do that with a for loop.)
@konforce: With that approach you'd have to make sure you are accessing valid entries (think of arrays with 0 or 1 elements), and will introduce more checks.
Either way you have to check for an empty array. With Brian's code, you get $min=100, $max = -1 on an empty array. So assuming you now have a non-empty array, there is no additional condition with for ($i = 1; $i < $len; ++$i). And in fact, you avoid the if/else on the first iteration (since it's skipped). Plus, foreach tends to be the worst performing way to iterate in PHP. But these are minor points, and I digress...
1
<?php
$array = array(
    "163",
    "630",
    "43",
    "924",
    "4",
    "54"
);
$arraycopy  = array_map('strlen',$array);
asort($arraycopy);

$min = reset($arraycopy);

//if you need a single 'minword'
$minword = $array[key($arraycopy)];
//if you need them all
$minwords = array_intersect_key($array,array_flip(array_keys($arraycopy,$min)));


$max = end($arraycopy);
//if you need a single 'maxword'
$maxword = $array[key($arraycopy)];
//if you need them all:
$maxwords = array_intersect_key($array,array_flip(array_keys($arraycopy,$max)));

var_dump($min,$max,$minword,$maxword,$minwords,$maxwords);

Comments

-2

For completion, here is a one-liner for maximum and minimum:

$maximum = max(array_map('strlen', $array));
$minimum = min(array_map('strlen', $array));

1 Comment

Mapping the same array twice with strlen to produce the same outcome is the nonsensical way of executing the accepted answer (posted 9 years earlier). No new value in this post.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.