Classificare matrice MultiDim: priorità se colonna contiene SOTTOSTRINGA, dell'ordine da una seconda colonna

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

Domanda

Al momento sto creando un metodo di ordinamento che consiste di valori da una query mysql.

Ecco una breve visione della matrice:

    Array
    (
        [0] => Array
            (
                ['id'] = 1;
                ['countries'] = 'EN,CH,SP';
            )
        [1] => Array
            (
                ['id'] = 2;
                ['countries'] = 'GE,SP,SV';
            )
    )

sono riuscito a fare un usort normale in base ai valori ID numerico, ma piuttosto vuole ordinare l'array dal contenuto dei "paesi" campo (se contiene una stringa insieme, un codice di paese, in questo caso) , e poi dal campo id.

Il seguente frammento è stata la mia prima idea di come farlo, ma non ho idea di come integrarla in una funzione di lavoro:

in_array('EN', explode(",",$a['countries']) );

Come si potrebbe fare?

Grazie!


Sono davvero arrivare da nessuna parte con questo purtroppo.

Ecco quello che ho per il momento, e la sua mi dà nulla, ma errori: uasort() [function.uasort]: Invalid comparison function

function compare($a, $b) {
    global $usercountry;

        if ( in_array($usercountry, $a['countries']) && in_array($usercountry, $a['countries']) ) {
            $return = 0;
        }

        else if (in_array($usercountry, $a['countries'])) {
            $return = 1;
        }

        else {
            $return = -1;
        }

        return $return;


        }

        $array= usort($array, "compare");

C'è qualcuno che mi potrebbe dare un suggerimento di come andare avanti con lui?

È stato utile?

Soluzione

Personalmente, vorrei utilizzare una funzione (anonimo) in combinazione con usort() personalizzato .

EDIT: Re - il tuo commento. Speriamo che questo vi metterà sulla strada giusta. Questa funzione fornisce la stessa priorità alla elementi che entrambi hanno EN o nessuno hanno EN, o la priorità rettificato quando solo uno ha EN.

usort($array,function ($a, $b) {
    $ac = strpos($a['countries'],'EN');
    $bc = strpos($b['countries'],'EN');
    if (($ac !== false && $bc !== false) || ($ac == false && $bc == false)) {
        return 0;
    }
    elseif ($ac !== false) {
        return 1;
    }
    else {
        return -1;
    }
});

Questa funzione, d'altra parte, dà la stessa priorità, se entrambi hanno EN, più alto se si ha EN, e fa un confronto di testo, se nessuno dei due ha IT.

usort($array,function ($a, $b) {
    $ac = strpos($a['countries'],'EN');
    $bc = strpos($b['countries'],'EN');
    if ($ac !== false && $bc !== false)) {
        return 0;
    }
    elseif ($ac !== false) {
        return 1;
    }
    elseif ($bc !== false) {
        return -1;
    }
    else {
        if ($a['countries'] == $b['countries']) {
            return 0;
        }
        elseif($a['countries'] > $b['countries']) {
            return 1;
        }
        else {
            return -1;
        }
    }
});

Ancora una volta, speriamo che questo vi darà abbastanza direzione per andare avanti da soli. Se si hanno problemi, non esitate a postare altri commenti e cercherò di aiutare. Una nota se si sta legando per confrontare molteplici proprietà con il peso:. Provare un blocco switch funky, per esempio

$ac = array_flip(explode(',',$a['countries']));
$bc = array_flip(explode(',',$b['countries']));
switch (true) {
    case array_key_exists('EN',$ac) && !array_key_exists('EN',$bc):
        return 1;
    case array_key_exists('DE',$ac) && !array_key_exists('EN',$bc) && !array_key_exists('EN',$bc):
        return 1;
    // and so on
}

Ulteriori modifiche!

In realtà, stavo pensando di più sul problema di ordinamento complessi, e sono venuto su con la seguente soluzione, per la vostra considerazione. Essa vi permetterà di definire classifiche numerici basati su parole chiave che appaiono nell'indice paesi. Ecco il codice, tra cui un esempio:

Esempio Array

$array = array(
    array(
        'countries' => 'EN,DE,SP',
    ),
    array(
        'countries' => 'EN,CH,SP',
    ),
    array(
        'countries' => 'DE,SP,CH',
    ),
    array(
        'countries' => 'DE,SV,SP',
    ),
    array(
        'countries' => 'EN,SP,FR',
    ),
    array(
        'countries' => 'DE,FR,CH',
    ),
    array(
        'countries' => 'CH,EN,SP',
    ),

);

Ordinamento routine

$rankings = array(
    'EN' => 10,
    'SP' => 8,
    'FR' => 7,
    'DE' => 5,
    'CH' => 3,
    'SV' => 1,
);
usort($array, function (&$a, &$b) use ($rankings) {
    if (isset($a['_score'])) {
        $aScore = $a['_score'];
    }
    else {
        $aScore = 0;
        $aCountries = explode(',',$a['countries']);
        foreach ($aCountries as $country) {
            if (isset($rankings[$country])) {
                $aScore += $rankings[$country];
            }
        }
        $a['_score'] = $aScore;
    }

    if (isset($b['_score'])) {
        $bScore = $b['_score'];
    }
    else {
        $bScore = 0;
        $bCountries = explode(',',$b['countries']);
        foreach ($bCountries as $country) {
            if (isset($rankings[$country])) {
                $bScore += $rankings[$country];
            }
        }
        $b['_score'] = $bScore;
    }
    if ($aScore == $bScore) {
        return 0;
    }
    elseif ($aScore > $bScore) {
        return -1;
    }
    else {
        return 1;
    }
});

Nota: Questo codice ordinare le entires più alto rango in cima matrice . Se si desidera un comportamento inverso, cambiare questo:

    elseif ($aScore > $bScore) {

a

    elseif ($aScore < $bScore) {

Si noti che il maggiore di è stato cambiato in un simbolo di meno-che. Questa modifica si tradurrà in voci più basso ranking da ordinare alla parte superiore della matrice . Spero che tutto questo aiuta!

NOTA ANCHE!

Questo codice fare una piccola modifica al vostro array, in quanto aggiunge l'elemento _score per ogni array. Speriamo che questo non è un problema, come memorizzando questo valore sono stato letteralmente in grado di aumentare la velocità di più del doppio (,00038-,00041 fino a 0,00016-0,00018 nei miei parametri di riferimento). In caso contrario, rimuovere i blocchi if che recuperano il valore memorizzato nella cache e lasciare che i contenuti dei blocchi else eseguire ogni volta, tranne ovviamente per la parte che memorizza il valore di punteggio.

A proposito, ecco una discarica var_export() dell'array dopo che è stato risolto:

array (
  0 => array (
    'countries' => 'EN,SP,FR',
    '_score' => 25,
  ),
  1 => array (
    'countries' => 'EN,DE,SP',
    '_score' => 23,
  ),
  2 => array (
    'countries' => 'EN,CH,SP',
    '_score' => 21,
  ),
  3 => array (
    'countries' => 'CH,EN,SP',
    '_score' => 21,
  ),
  4 => array (
    'countries' => 'DE,SP,CH',
    '_score' => 16,
  ),
  5 => array (
    'countries' => 'DE,FR,CH',
    '_score' => 15,
  ),
  6 => array (
    'countries' => 'DE,SV,SP',
    '_score' => 14,
  ),
)

Enjoy!

Altri suggerimenti

Infine trovato questo meraviglioso funzione in PHP.net:

        function array_msort($array, $cols)
        {
            $colarr = array();
            foreach ($cols as $col => $order) {
                $colarr[$col] = array();
                foreach ($array as $k => $row) { $colarr[$col]['_'.$k] = strtolower($row[$col]); }
            }
            $eval = 'array_multisort(';
            foreach ($cols as $col => $order) {
                $eval .= '$colarr[\''.$col.'\'],'.$order.',';
            }
            $eval = substr($eval,0,-1).');';
            eval($eval);
            $ret = array();
            foreach ($colarr as $col => $arr) {
                foreach ($arr as $k => $v) {
                    $k = substr($k,1);
                    if (!isset($ret[$k])) $ret[$k] = $array[$k];
                    $ret[$k][$col] = $array[$k][$col];
                }
            }
            return $ret;

        }

Questo è il modo in ogni paese si presenta come: $ Array [ 'paesi'] = in_array ($ ago, $ pagliaio);                         }

$array = $array = array_msort($array, array('countries'=>SORT_DESC, 'id'=>SORT_ASC));

Grazie a tutti per il vostro aiuto!

Si potrebbe considerare array_walk e array_walk_recursive e array_map , che se combinati insieme forse per arrivare a fare ciò che si vuole fare.

Prova con array_mulisort .

uasort per vedere come utilizzare un utente definito funzione di confronto.

  

Al momento sto creando un metodo di ordinamento che consiste di valori da una query mysql.

VERITÀ:
Usando qualcosa di diverso da MySQL per ordinare il vostro set di risultati sarà meno efficiente (con php, una chiamata o usort() array_multisort() sarà più contorta e difficile da mantenere) e quindi inadeguato .

SQL: ( Demo )

ORDER BY IF(LOCATE('EN', countries), 0, 1), id;

Questa priorità valori di colonna countries che contengono EN poi sorta sul id ASC.


Per chi non sta gestendo il risultato sql impostare o non in grado di manipolare la query per qualche motivo, sottoscrivo usort(). PHP7 offre un bellissimo nuovo operatore che esegue un confronto e restituisce uno dei tre valori (-1, 0, 1). Questo operatore è affettuosamente chiamato "operatore nave spaziale" e si presenta così <=>.

PHP: ( Demo )

$test = [
    ['id' => 1, 'countries' => 'EN,CH,SP'],
    ['id' => 2, 'countries' => 'GE,SP,SV'],
    ['id' => 3, 'countries' => 'PR,SP,IT'],
    ['id' => 4, 'countries' => 'EN'],
    ['id' => 5, 'countries' => 'SP,EN'],
    ['id' => 6, 'countries' => 'SV,SP,EN'],
    ['id' => 7, 'countries' => 'GE,SP'],
    ['id' => 8, 'countries' => 'FR'],
    ['id' => 9, 'countries' => 'RU,EN'],
    ['id' => 10, 'countries' => 'EN,SP,IT'],
    ['id' => 11, 'countries' => 'SP,GR'],
    ['id' => 12, 'countries' => 'GR,EN']
];

usort($test, function($a, $b) {
    return [strpos($a['countries'], 'EN') === false, $a['id']] <=> [strpos($b['countries'], 'EN') === false, $b['id']];
});

var_export($test);

Output:

array (
  0 => 
  array (
    'id' => 1,
    'countries' => 'EN,CH,SP',
  ),
  1 => 
  array (
    'id' => 4,
    'countries' => 'EN',
  ),
  2 => 
  array (
    'id' => 5,
    'countries' => 'SP,EN',
  ),
  3 => 
  array (
    'id' => 6,
    'countries' => 'SV,SP,EN',
  ),
  4 => 
  array (
    'id' => 9,
    'countries' => 'RU,EN',
  ),
  5 => 
  array (
    'id' => 10,
    'countries' => 'EN,SP,IT',
  ),
  6 => 
  array (
    'id' => 12,
    'countries' => 'GR,EN',
  ),
  7 => 
  array (
    'id' => 2,
    'countries' => 'GE,SP,SV',
  ),
  8 => 
  array (
    'id' => 3,
    'countries' => 'PR,SP,IT',
  ),
  9 => 
  array (
    'id' => 7,
    'countries' => 'GE,SP',
  ),
  10 => 
  array (
    'id' => 8,
    'countries' => 'FR',
  ),
  11 => 
  array (
    'id' => 11,
    'countries' => 'SP,GR',
  ),
)

Gli elementi di campo su entrambi i lati dell'operatore navicella vengono valutati da sinistra a destra (leftside [0] vs rightside [0], poi si spostano sulla coppia di [1] valori se c'è un "legare" tra i due [0] valori).

Se il === false guarda indietro, lasciatemi spiegare ...

Se EN si trova nella stringa paesi, la condizione valuterà come false. Quando si confrontano true e false, ricordate che true equivale a 1 e false equivale a 0. Vogliamo ASC ordinamento, quindi vogliamo mettere falsi esiti prima di veri risultati, le stringhe contenenti ergo EN necessità per restituire false. Speriamo che chiarisce la logica.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top