كيف يمكنني فرز مصفوفة متعددة الأبعاد في PHP [نسخة مكررة]

StackOverflow https://stackoverflow.com/questions/96759

سؤال

هذا السؤال لديه بالفعل إجابة هنا:

لدي بيانات CSV تم تحميلها في مصفوفة متعددة الأبعاد.وبهذه الطريقة يكون كل "صف" بمثابة سجل، ويحتوي كل "عمود" على نفس نوع البيانات.أنا أستخدم الوظيفة أدناه لتحميل ملف CSV الخاص بي.

function f_parse_csv($file, $longest, $delimiter)
{
  $mdarray = array();
  $file    = fopen($file, "r");
  while ($line = fgetcsv($file, $longest, $delimiter))
  {
    array_push($mdarray, $line);
  }
  fclose($file);
  return $mdarray;
}

أحتاج إلى أن أكون قادرًا على تحديد عمود للفرز بحيث يعيد ترتيب الصفوف.يحتوي أحد الأعمدة على معلومات التاريخ بتنسيق Y-m-d H:i:s وأود أن أكون قادرًا على الفرز بحيث يكون التاريخ الأحدث هو الصف الأول.

هل كانت مفيدة؟

المحلول

يمكنك استخدام array_multisort()

جرب شيئًا مثل هذا:

foreach ($mdarray as $key => $row) {
    // replace 0 with the field's index/key
    $dates[$key]  = $row[0];
}

array_multisort($dates, SORT_DESC, $mdarray);

بالنسبة إلى PHP >= 5.5.0، ما عليك سوى استخراج العمود للفرز حسبه.لا حاجة للحلقة:

array_multisort(array_column($mdarray, 0), SORT_DESC, $mdarray);

نصائح أخرى

مقدمة:حل معمم جدًا لـ PHP 5.3+

أود إضافة الحل الخاص بي هنا، لأنه يقدم ميزات لا توفرها الإجابات الأخرى.

على وجه التحديد، تشمل مزايا هذا الحل ما يلي:

  1. إنه قابلة لإعادة الاستخدام:قمت بتحديد عمود الفرز كمتغير بدلاً من ترميزه.
  2. إنه مرن:يمكنك تحديد أعمدة فرز متعددة (بالعدد الذي تريده) - يتم استخدام أعمدة إضافية كعناصر فاصلة بين العناصر التي تقارن بشكل متساوٍ في البداية.
  3. إنه تفريغ:يمكنك تحديد أنه يجب عكس الفرز - بشكل فردي لكل عمود.
  4. إنه توسع:إذا كانت مجموعة البيانات تحتوي على أعمدة لا يمكن مقارنتها بطريقة "غبية" (على سبيل المثال.سلاسل التاريخ) يمكنك أيضًا تحديد كيفية تحويل هذه العناصر إلى قيمة يمكن مقارنتها مباشرة (على سبيل المثال.أ DateTime مثال).
  5. إنه النقابي إذا كنت تريد:يعتني هذا الرمز بفرز العناصر، ولكن أنت حدد وظيفة الفرز الفعلية (usort أو uasort).
  6. وأخيرا، فإنه لا يستخدم array_multisort:بينما array_multisort إنه أمر مريح، فهو يعتمد على إنشاء إسقاط لجميع بيانات الإدخال الخاصة بك قبل الفرز.يستهلك هذا الوقت والذاكرة وقد يكون ببساطة محظورًا إذا كانت مجموعة البيانات الخاصة بك كبيرة.

الرمز

function make_comparer() {
    // Normalize criteria up front so that the comparer finds everything tidy
    $criteria = func_get_args();
    foreach ($criteria as $index => $criterion) {
        $criteria[$index] = is_array($criterion)
            ? array_pad($criterion, 3, null)
            : array($criterion, SORT_ASC, null);
    }

    return function($first, $second) use (&$criteria) {
        foreach ($criteria as $criterion) {
            // How will we compare this round?
            list($column, $sortOrder, $projection) = $criterion;
            $sortOrder = $sortOrder === SORT_DESC ? -1 : 1;

            // If a projection was defined project the values now
            if ($projection) {
                $lhs = call_user_func($projection, $first[$column]);
                $rhs = call_user_func($projection, $second[$column]);
            }
            else {
                $lhs = $first[$column];
                $rhs = $second[$column];
            }

            // Do the actual comparison; do not return if equal
            if ($lhs < $rhs) {
                return -1 * $sortOrder;
            }
            else if ($lhs > $rhs) {
                return 1 * $sortOrder;
            }
        }

        return 0; // tiebreakers exhausted, so $first == $second
    };
}

كيف تستعمل

سأقدم خلال هذا القسم روابط تقوم بفرز مجموعة البيانات النموذجية هذه:

$data = array(
    array('zz', 'name' => 'Jack', 'number' => 22, 'birthday' => '12/03/1980'),
    array('xx', 'name' => 'Adam', 'number' => 16, 'birthday' => '01/12/1979'),
    array('aa', 'name' => 'Paul', 'number' => 16, 'birthday' => '03/11/1987'),
    array('cc', 'name' => 'Helen', 'number' => 44, 'birthday' => '24/06/1967'),
);

أساسيات

الوظيفة make_comparer يقبل عددًا متغيرًا من الوسائط التي تحدد الفرز المطلوب وترجع دالة من المفترض أن تستخدمها كوسيطة لها usort أو uasort.

إن أبسط حالة استخدام هي تمرير المفتاح الذي ترغب في استخدامه لمقارنة عناصر البيانات.على سبيل المثال، للفرز $data بواسطة name العنصر الذي ستفعله

usort($data, make_comparer('name'));

شاهده على أرض الواقع.

يمكن أن يكون المفتاح أيضًا رقمًا إذا كانت العناصر عبارة عن صفائف مفهرسة رقميًا.على سبيل المثال في السؤال، سيكون هذا

usort($data, make_comparer(0)); // 0 = first numerically indexed column

شاهده على أرض الواقع.

أعمدة فرز متعددة

يمكنك تحديد أعمدة فرز متعددة عن طريق تمرير معلمات إضافية إليها make_comparer.على سبيل المثال، للفرز حسب "الرقم" ثم حسب العمود المفهرس صفرًا:

usort($data, make_comparer('number', 0));

شاهده على أرض الواقع.

الخيارات المتقدمة

تتوفر المزيد من الميزات المتقدمة إذا قمت بتحديد عمود فرز كمصفوفة بدلاً من سلسلة بسيطة.يجب أن تتم فهرسة هذه المصفوفة رقميًا، ويجب أن تحتوي على العناصر التالية:

0 => the column name to sort on (mandatory)
1 => either SORT_ASC or SORT_DESC (optional)
2 => a projection function (optional)

دعونا نرى كيف يمكننا استخدام هذه الميزات.

فرز عكسي

للفرز حسب الاسم تنازليا:

usort($data, make_comparer(['name', SORT_DESC]));

شاهده على أرض الواقع.

للفرز حسب الرقم تنازليًا ثم حسب الاسم تنازليًا:

usort($data, make_comparer(['number', SORT_DESC], ['name', SORT_DESC]));

شاهده على أرض الواقع.

التوقعات المخصصة

في بعض السيناريوهات، قد تحتاج إلى الفرز حسب عمود لا تتناسب قيمه مع الفرز.يناسب عمود "عيد الميلاد" في مجموعة البيانات النموذجية هذا الوصف:ليس من المنطقي مقارنة أعياد الميلاد كسلاسل (لأنه على سبيل المثال.يأتي "01/01/1980" قبل "10/10/1970").في هذه الحالة نريد تحديد كيفية القيام بذلك مشروع البيانات الفعلية إلى النموذج الذي يستطيع مقارنتها مباشرة بالدلالات المطلوبة.

يمكن تحديد التوقعات على أنها أي نوع من قابل للاستدعاء:كسلاسل أو صفائف أو وظائف مجهولة.من المفترض أن يقبل الإسقاط وسيطة واحدة ويعيد شكلها المتوقع.

تجدر الإشارة إلى أنه على الرغم من أن الإسقاطات تشبه وظائف المقارنة المخصصة المستخدمة مع usort والعائلة، فهي أبسط (تحتاج فقط إلى تحويل قيمة إلى أخرى) والاستفادة من جميع الوظائف المضمنة بالفعل make_comparer.

لنقم بفرز مجموعة البيانات النموذجية بدون إسقاط ونرى ما سيحدث:

usort($data, make_comparer('birthday'));

شاهده على أرض الواقع.

ولم تكن تلك هي النتيجة المرجوة.ولكن يمكننا استخدامها date_create كإسقاط:

usort($data, make_comparer(['birthday', SORT_ASC, 'date_create']));

شاهده على أرض الواقع.

وهذا هو الترتيب الصحيح الذي أردناه.

هناك العديد من الأشياء التي يمكن للتوقعات تحقيقها.على سبيل المثال، هناك طريقة سريعة للحصول على فرز غير حساس لحالة الأحرف وهي استخدام strtolower كإسقاط.

ومع ذلك، يجب أن أذكر أيضًا أنه من الأفضل عدم استخدام التوقعات إذا كانت مجموعة البيانات الخاصة بك كبيرة:في هذه الحالة، سيكون من الأسرع بكثير عرض جميع بياناتك يدويًا مقدمًا ثم فرزها دون استخدام العرض، على الرغم من أن القيام بذلك سيؤدي إلى استبدال استخدام الذاكرة المتزايد بسرعة فرز أسرع.

وأخيرًا، إليك مثال يستخدم كافة الميزات:يتم الفرز أولاً حسب الرقم تنازليًا، ثم حسب تاريخ الميلاد تصاعديًا:

usort($data, make_comparer(
    ['number', SORT_DESC],
    ['birthday', SORT_ASC, 'date_create']
));

شاهده على أرض الواقع.

مع com.usort.فيما يلي حل عام يمكنك استخدامه لأعمدة مختلفة:

class TableSorter {
  protected $column;
  function __construct($column) {
    $this->column = $column;
  }
  function sort($table) {
    usort($table, array($this, 'compare'));
    return $table;
  }
  function compare($a, $b) {
    if ($a[$this->column] == $b[$this->column]) {
      return 0;
    }
    return ($a[$this->column] < $b[$this->column]) ? -1 : 1;
  }
}

للفرز حسب العمود الأول:

$sorter = new TableSorter(0); // sort by first column
$mdarray = $sorter->sort($mdarray);

فرز صفوف متعددة باستخدام الإغلاق

إليك طريقة أخرى تستخدم uasort() ووظيفة رد الاتصال المجهولة (الإغلاق).لقد استخدمت هذه الوظيفة بانتظام. مطلوب PHP 5.3 – لا مزيد من التبعيات!

/**
 * Sorting array of associative arrays - multiple row sorting using a closure.
 * See also: http://the-art-of-web.com/php/sortarray/
 *
 * @param array $data input-array
 * @param string|array $fields array-keys
 * @license Public Domain
 * @return array
 */
function sortArray( $data, $field ) {
    $field = (array) $field;
    uasort( $data, function($a, $b) use($field) {
        $retval = 0;
        foreach( $field as $fieldname ) {
            if( $retval == 0 ) $retval = strnatcmp( $a[$fieldname], $b[$fieldname] );
        }
        return $retval;
    } );
    return $data;
}

/* example */
$data = array(
    array( "firstname" => "Mary", "lastname" => "Johnson", "age" => 25 ),
    array( "firstname" => "Amanda", "lastname" => "Miller", "age" => 18 ),
    array( "firstname" => "James", "lastname" => "Brown", "age" => 31 ),
    array( "firstname" => "Patricia", "lastname" => "Williams", "age" => 7 ),
    array( "firstname" => "Michael", "lastname" => "Davis", "age" => 43 ),
    array( "firstname" => "Sarah", "lastname" => "Miller", "age" => 24 ),
    array( "firstname" => "Patrick", "lastname" => "Miller", "age" => 27 )
);

$data = sortArray( $data, 'age' );
$data = sortArray( $data, array( 'lastname', 'firstname' ) );

أعلم أنه قد مر عامان منذ طرح هذا السؤال والإجابة عليه، ولكن إليك وظيفة أخرى تقوم بفرز مصفوفة ثنائية الأبعاد.فهو يقبل عددًا متغيرًا من الوسائط، مما يسمح لك بتمرير أكثر من مفتاح واحد (أي اسم العمود) للفرز حسب.مطلوب PHP 5.3.

function sort_multi_array ($array, $key)
{
  $keys = array();
  for ($i=1;$i<func_num_args();$i++) {
    $keys[$i-1] = func_get_arg($i);
  }

  // create a custom search function to pass to usort
  $func = function ($a, $b) use ($keys) {
    for ($i=0;$i<count($keys);$i++) {
      if ($a[$keys[$i]] != $b[$keys[$i]]) {
        return ($a[$keys[$i]] < $b[$keys[$i]]) ? -1 : 1;
      }
    }
    return 0;
  };

  usort($array, $func);

  return $array;
}

جربه هنا: http://www.exorithm.com/algorithm/view/sort_multi_array

function cmp($a, $b)
{
$p1 = $a['price'];
$p2 = $b['price'];
return (float)$p1 > (float)$p2;
}
uasort($my_array, "cmp");

http://qaify.com/sort-an-array-of-associative-arrays-by-value-of-given-key-in-php/

وظيفة "Usort" هي إجابتك.
http://php.net/usort

فيما يلي فئة php4/php5 التي ستقوم بفرز حقل واحد أو أكثر:

// a sorter class
//  php4 and php5 compatible
class Sorter {

  var $sort_fields;
  var $backwards = false;
  var $numeric = false;

  function sort() {
    $args = func_get_args();
    $array = $args[0];
    if (!$array) return array();
    $this->sort_fields = array_slice($args, 1);
    if (!$this->sort_fields) return $array();

    if ($this->numeric) {
      usort($array, array($this, 'numericCompare'));
    } else {
      usort($array, array($this, 'stringCompare'));
    }
    return $array;
  }

  function numericCompare($a, $b) {
    foreach($this->sort_fields as $sort_field) {
      if ($a[$sort_field] == $b[$sort_field]) {
        continue;
      }
      return ($a[$sort_field] < $b[$sort_field]) ? ($this->backwards ? 1 : -1) : ($this->backwards ? -1 : 1);
    }
    return 0;
  }

  function stringCompare($a, $b) {
    foreach($this->sort_fields as $sort_field) {
      $cmp_result = strcasecmp($a[$sort_field], $b[$sort_field]);
      if ($cmp_result == 0) continue;

      return ($this->backwards ? -$cmp_result : $cmp_result);
    }
    return 0;
  }
}

/////////////////////
// usage examples

// some starting data
$start_data = array(
  array('first_name' => 'John', 'last_name' => 'Smith', 'age' => 10),
  array('first_name' => 'Joe', 'last_name' => 'Smith', 'age' => 11),
  array('first_name' => 'Jake', 'last_name' => 'Xample', 'age' => 9),
);

// sort by last_name, then first_name
$sorter = new Sorter();
print_r($sorter->sort($start_data, 'last_name', 'first_name'));

// sort by first_name, then last_name
$sorter = new Sorter();
print_r($sorter->sort($start_data, 'first_name', 'last_name'));

// sort by last_name, then first_name (backwards)
$sorter = new Sorter();
$sorter->backwards = true;
print_r($sorter->sort($start_data, 'last_name', 'first_name'));

// sort numerically by age
$sorter = new Sorter();
$sorter->numeric = true;
print_r($sorter->sort($start_data, 'age'));

قبل أن أتمكن من تشغيل فئة TableSorter، توصلت إلى وظيفة تعتمد على ماذا شينهان قد قدمت.

function sort2d_bycolumn($array, $column, $method, $has_header)
  {
  if ($has_header)  $header = array_shift($array);
  foreach ($array as $key => $row) {
    $narray[$key]  = $row[$column]; 
    }
  array_multisort($narray, $method, $array);
  if ($has_header) array_unshift($array, $header);
  return $array;
  }
  • $array هو MD Array الذي تريد فرزه.
  • $column هو العمود الذي ترغب في الفرز حسبه.
  • الطريقة $ هي الطريقة التي تريد إجراء الفرز بها، مثل SORT_DESC
  • يتم تعيين $has_header على true إذا كان الصف الأول يحتوي على قيم رأس لا تريد فرزها.

لقد جربت العديد من إجابات array_multisort() و usort() الشائعة ولم ينجح أي منها معي.يتم خلط البيانات فقط ويكون الرمز غير قابل للقراءة.وإليك حل سريع القذرة.تحذير:استخدم هذا فقط إذا كنت متأكدًا من أن المحدد المارق لن يعود ليطاردك لاحقًا!

لنفترض أن كل صف في المصفوفة المتعددة يبدو كما يلي:الاسم، المادة 1، المادة 2:

// Sort by name, pull the other stuff along for the ride
foreach ($names_stuff as $name_stuff) {
    // To sort by stuff1, that would be first in the contatenation
    $sorted_names[] = $name_stuff[0] .','. name_stuff[1] .','. $name_stuff[2];
}
sort($sorted_names, SORT_STRING);

هل تحتاج إلى إعادة أغراضك بالترتيب الأبجدي؟

foreach ($sorted_names as $sorted_name) {
    $name_stuff = explode(',',$sorted_name);
    // use your $name_stuff[0] 
    // use your $name_stuff[1] 
    // ... 
}

نعم، انها قذرة.لكن سهل للغاية، لن يجعل رأسك ينفجر.

أفضل استخدام array_multisort.انظر الوثائقهنا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top