2
\$\begingroup\$

I'm running hundreds of curl operations in parallel to my own API, the response array that I get is out of order (the reason being that the first one to finish gets added to the response array first). I need to sort these back so they correspond to the input array, in order to do that I have already written a function as shown below.

I'm interested if there is a more efficient way to do this or not.

Note: The sample shows what I mean, however the input array is usually more than 100 elements long.

Sample input:

$input = array (
   'http://www.yandex.ru',
   'http://www.mail.ru',
   'http://www.google.com'
)

Response:

$response = array (
    array('http://www.mail.ru', 200, 'some other string'),
    array('http://www.yandex.ru', 200, 'some string'),
    array('http://www.google.com', 200, 'yet another string')
)

Function:

function re_order($original, $scrambled) {
    foreach ($scrambled as $url_response) {
       $key = array_search($url_response[0],$original);
       $result[$key] = $url_response;
    }
    ksort($result);
    return $result;
}

Resulting array after calling the function (i.e. desired result):

$result = array (
    array('http://www.yandex.ru', 200, 'some string'),
    array('http://www.mail.ru', 200, 'some other string'),
    array('http://www.google.com', 200, 'yet another string')
)  
\$\endgroup\$
4
  • 3
    \$\begingroup\$ Do the URLs in the input and response actually match exactly? If so, you could key the response array by the URL and stop there without having to do a sort. Since your input already is already in order, you can loop over it, get the URL key, then use that to access the response array. \$\endgroup\$
    – Corbin
    Commented Oct 25, 2014 at 4:58
  • \$\begingroup\$ Why you want to sort it? As you say, requests are done parallel, and you have host in result array. \$\endgroup\$
    – Styx
    Commented Nov 7, 2014 at 20:12
  • \$\begingroup\$ @Styx, well the URL is only there in the result array to see what result I'm getting ideally I don't need it or the 200 there. Since I wrote this I've added a new function to my parallel curl that carries the original key whilst processing, then sorts out the result array before returning it. (results can come from either file cache, or from curl requests, and can be files or web pages) \$\endgroup\$ Commented Nov 9, 2014 at 14:04
  • \$\begingroup\$ If you don't need url (input array), then why are you need sorted result? I would do this like @Corbin said. No sorting. \$\endgroup\$
    – Styx
    Commented Nov 9, 2014 at 18:17

2 Answers 2

1
\$\begingroup\$

Based on the comments I'm not entirely sure what you ended up doing so I thought I'd provide a general example in case other folks drop by.

Sample Code and Output

<?php

function random_finish()
{
  $list = array();
  $list[2001] = array(200,'result for URL ID 2001');
  $list[2002] = array(200,'result for URL ID 2002');
  $list[2003] = array(200,'result for URL ID 2003');
  $list[2004] = array(200,'result for URL ID 2004');
  $list[2005] = array(200,'result for URL ID 2005');

  $used = array();
  while ( count($used) < count($list) )
  {
    $r = mt_rand(2001,2005);
    if ( !isset($used[$r]) )
    {
      echo "... result for URL ID $r retrieved<br>\n";
      $used[$r] = $list[$r];
    }
  }

  return $used;
}

echo "<br>\n";
echo "Randomized result order...<br>\n";
$result = random_finish();

The above is meant to emulate random API response order and provides a result similar to the following:

Randomized result order...
... result for URL ID 2005 retrieved
... result for URL ID 2004 retrieved
... result for URL ID 2001 retrieved
... result for URL ID 2003 retrieved
... result for URL ID 2002 retrieved

Here, we emulate "using" the data as returned by using the key value to know which URL apply the response to. The key could be a globally unique URL ID or it could be a request sequence number:

echo "<br>\n";
echo "Use as is via request key...<br>\n";
foreach ($result as $key => $val)
  echo "... URL ID $key with result code " . $val[0] . " and text '" . $val[1] . "'<br>\n";

This matches the random response order above:

Use as is via request key...
... URL ID 2005 with result code 200 and text 'result for URL ID 2005'
... URL ID 2004 with result code 200 and text 'result for URL ID 2004'
... URL ID 2001 with result code 200 and text 'result for URL ID 2001'
... URL ID 2003 with result code 200 and text 'result for URL ID 2003'
... URL ID 2002 with result code 200 and text 'result for URL ID 2002'

If a reordering was needed it can be done efficiently by using the built in function ksort:

echo "<br>\n";
echo "If needed, list after ksort...<br>\n";
ksort($result);
foreach ($result as $key => $val)
  echo "... URL ID $key with result code " . $val[0] . " and text '" . $val[1] . "'<br>\n";

Which gives us an array (or list) of results sorted on the array key. This could be a request ordering value though it would be nice to have a unique ID for each URL to be processed so that the ordering would not matter.

If needed, list after ksort...
... URL ID 2001 with result code 200 and text 'result for URL ID 2001'
... URL ID 2002 with result code 200 and text 'result for URL ID 2002'
... URL ID 2003 with result code 200 and text 'result for URL ID 2003'
... URL ID 2004 with result code 200 and text 'result for URL ID 2004'
... URL ID 2005 with result code 200 and text 'result for URL ID 2005'

Some key points from a code review standpoint:

  • Using a unique ID for URLs can eliminate the need to do any sorting.

  • Sorting using a simple built-in function is likely to be more efficient than repeated lookup activities (especially for larger lists).

  • Choosing a key for an array instead of accepting the default numeric keys can provide a lot of value in various situations.

In a nutshell, solution design can have a huge impact on solution complexity and efficiency.

\$\endgroup\$
1
\$\begingroup\$

You don't need to call any sort() functions because your input and response arrays have a 1-to-1 relationship.

  1. Flip your input array so that the values become keys.
  2. Use array_column()'s third parameter to assign temporary keys to the response array.
  3. Now that the two arrays have relatable keys, you can just overwrite the flipped input array with the newly-keyed response array.
  4. Optionally, you can chose to re-index the result by calling array_values().

Easy done.

Code: (Demo)

$input = [
   'http://www.yandex.ru',
   'http://www.mail.ru',
   'http://www.google.com'
];

$response = [
    ['http://www.mail.ru', 200, 'some other string'],
    ['http://www.yandex.ru', 200, 'some string'],
    ['http://www.google.com', 200, 'yet another string']
];

var_export(
    array_values(
        array_replace(
            array_flip($input),
            array_column($response, null, 0)
        )
    )
);

Output:

array (
  0 => 
  array (
    0 => 'http://www.yandex.ru',
    1 => 200,
    2 => 'some string',
  ),
  1 => 
  array (
    0 => 'http://www.mail.ru',
    1 => 200,
    2 => 'some other string',
  ),
  2 => 
  array (
    0 => 'http://www.google.com',
    1 => 200,
    2 => 'yet another string',
  ),
)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.