[PHP]Sortowanie według własnego alfabetu

0

Witam.
Przy użyciu zwykłej funkcji sortowania tablic (array_multisort), kompilator posortował mi jej elementy alfabetycznie. Ja chciałbym nieco zmienić szyk tych elementów, tzn. tak jakby przenieść wcześniej w alfabecie niektóre znaki. Chciałbym, aby podkreślnik _ był przed literami (elementy nie zawierają polskich znaków).
Chciałbym też, aby razem z daną tablicą sortowały się w ten sam sposób inne tablice, czyli wypadałoby zamieszać w to funkcję array_multisort.
Aktualnie mam taki kod:

$array_lowercase = array_map('strtolower', $name);
array_multisort($array_lowercase, $name, $id, $body, $klasy);

Całość działałaby tak:

$tablica = array("ala", "alo", "ali", "al_");

powinna zostać posortowana tak:

al_
ala
ali
alo

Znalazłem coś na ten temat na algorytmy.pl (sortowanie według własnego alfabetu), jednak strona jest płatna, a ja aktualnie nie mam tyle pieniędzy, żeby rozdawać je na prawo i lewo. Może wy coś poradzicie? Może funkcja uksort()? usort()?
Proszę pamiętać, że równolegle powinny się sortować trzy tablice, zachowując wszędzie taką samą kolejność w stosunku do stanu początkowego (czyli: jeśli element 1 z pierwszej tablicy idzie na trzecie miejsce, to to samo się dzieje z elementami '1' w pozostałych tablicach).

Ja na razie mam taki kod:

<?php
header('Content-Type: text/html; charset=utf-8');
$xml = simplexml_load_file("new2.txt");
$ile = count($xml -> string);
foreach ($xml -> string as $value)
{
	  $id[] = (string)$value -> id;
	  $name[] = (string)$value -> name;
	  $body[] = (string)$value -> body;
	  $klasy[] = (preg_match("/STR_SKILL_([a-z]{2})_(.+)/i", ((string)$value -> name ? preg_replace("/STR_SKILL_([a-z]{2})_(.+)/i", "\\1", (string)$value -> name) : "");
}
$array_lowercase = array_map('strtolower', $name);
array_multisort($array_lowercase, $name, $id, $body, $klasy);
$zapytanie = "";
for($i=0; $i < $ile; $i++)
{
  	$dab = 0;
	$tekst = "";
	$tresc = "\"";
	$tekst .= "(\"".$id[$i]."\", \"".$name[$i]."\", \"".$klasy[$i]."\", \"".$body[$i]."\", ";
	$i++;
	if (preg_match("/(.+)_DESC_Abnormal$/i", $name[$i+1]))
	{
		$tekst .= "\"".$id[$i+1]."\", \"".$name[$i+1]."\", ";
		$tresc .= $body[$i+1]."=====";
		$dab = 1;
	} else if (preg_match("/(.+)_Abnormal$/i", $name[$i]) || preg_match("/(.+)_effect$/i", $name[$i]))
	{
		$tekst .= "\"".$id[$i]."\", \"".$name[$i]."\", ";
		$tresc .= $body[$i]."=====";
	} else {
		$tekst .= "\"\", \"\", ";
	}
	if (preg_match("/(.+)_DESC$/i", $name[$i]) || preg_match("/(.+)_area$/i", $name[$i]))
	{
		$tekst .= "\"".$id[$i]."\", \"".$name[$i]."\", ";
		$tresc .= $body[$i]."\")";
		if ($dab) $i++;
	} else {
		$tekst .= "\"\", ";
		$tresc .= "\")";
	}
	$tekst .= $tresc;
	$zapytanie .= "$tekst, ";
}
$query = "INSERT INTO sl_skille (`id_z_gry`, `name`, `klasa`, `nazwa_ang`, `id_z_gry_abnormal`, `name_abnormal`, `id_z_gry_desc`, `name_desc`, `opis_ang`) VALUES ". substr($zapytanie, 0, -2).";";
print_r($name); echo "<br/><br/>";
echo $query;

?>

Działa on poprawnie, jednak sortowanie jest nieco inne, niż bym sobie tego życzył.
Wynikiem jest tablica zawierająca na przykład takie elementy:

[30] => STR_ACH_StatupAr_Lr_Nr_Officer [31] => STR_ACH_StatupAr_Lr_Nr_Officer_Abnormal [32] => STR_ACH_StatupAr_Lr_Nr_Officer_DESC [33] => STR_ACH_StatupAr_Lr_Nr_OfficerF [34] => STR_ACH_StatupAr_Lr_Nr_OfficerF2 [35] => STR_ACH_StatupAr_Lr_Nr_OfficerF2_DESC [36] => STR_ACH_StatupAr_Lr_Nr_OfficerF_Abnormal [37] => STR_ACH_StatupAr_Lr_Nr_OfficerF_DESC [38] => STR_ACH_StatupAr_Lr_Nr_QuarterMaster

Zależy mi na tym, aby po 33 znalazł się 37, czyli aby najpierw sortowało F_ (lub po prostu coś zawierającego podkreślnik), a dopiero później resztę, czyli F2, itd.

0

Funkcja usort() (oraz uksort() i uasort()) pozwalają ci stworzyć własną funkcję sortującą. I chyba na tym się będziesz musiał oprzeć:

http://pl.php.net/manual/en/function.usort.php

usort() działa w ten sposób, że przy każdym porównaniu wywoływana jest twoja funkcja, która dostaje dwa parametry: $a i $b i zwraca 0 gdy są równe, -1 gdy a < b i 1 gdy a > b. Niestety standardowe porównywanie pierwszeństwa stringów oparte jest o tablicę ASCII i to też będziesz musiał zrealizować po swojemu.

  • stwórz tablicę z kolejnością znaków tak jak chcesz by były w twoim "alfabecie"
  • porównuj znak po znaku (od początku) stringi ($a i $b) i przy pierwszej różnicy już możesz wyznaczyć która ze zmiennych będzie "pierwsza" - na podstawie tej tablicy (dokładnie który ze znaków ma w niej mniejszy indeks)

Niestety nie ma "własnego" rozwiązania dla array_multisort, trzeba samemu zrobić coś takiego jak multisortowanie.

0

Dzięki za podpowiedź - taki sposób ma sens.
Zabrałem się za jego robienie, jednak efekty nie są zbyt satysfakcjonujące.
Oto kod:

	$tablica[] = "str_skill_wa_equip_enhanceddagger2";
	$tablica[] = "str_skill_wa_equip_enhanceddagger_desc";
	$tablica[] = "str_skill_wa_equip_enhanceddagger";
	$tablica[] = "str_skill_wa_equip_enhanceddagger2_desc";
	$tablica[] = "str_skill_wa_equip_enhanceddagger_desc_abnormal";
$kolejnosc = array('_', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
	function cmp ($a, $b)
	{
	  global $kolejnosc;
   		$a_t = str_split($a);
   		$b_t = str_split($b);
		$ilosc = (count($a_t) > count($b_t) ? count($b_t) : count($a_t));
		$a = 0; $b = 0;
		for ($i=0; $i < $ilosc; $i++)
		{
		  echo $a_t[$i]." - ".$b_t[$i]."<br/>";
    			if (array_search($a_t[$i], $kolejnosc) < array_search($b_t[$i], $kolejnosc))
    			{
	        		$a++; echo "jeden ";
	      		} else if (array_search($a_t[$i], $kolejnosc) > array_search($b_t[$i], $kolejnosc))
	      		{
	          		$b++; echo "dwa ";
			}
    		}
    		if ($a > $b)
    		{
        		echo "trzy "; return 1;
		} else if ($a < $b)
		{
    			echo "cztery "; return -1;
  		} else if (count($a_t) < count($b_t))
   		{
       			echo "piec "; return -1;
		} else if (count($a_t) > count($b_t))
		{
		  	echo "szesc "; return 1;
		} else {
			echo "siedem "; return 0;
		}
   	}
	usort($tablica, "cmp");
	print_r($tablica);

Te wszystkie "echa" są oczywiście dla kontroli. Wydaje mi się, że funkcja porównuje tylko dwie sąsiednie nazwy, a przecież nie o to tu chodzi. Chciałbym, aby sortowała wszystkie elementy z tablicy.

0

twoja funkcja jest wywoływana w środku quick sorta i będzie wywołana minimalną praktycznie liczbę razy
to że nie porównuje wszystkiego ze wszystkim jest normalne bo ten algorytm wykorzystuje dane uzyskane wcześniej (np skoro a < b i b < c to wiadomo że a < c)
w związku z tym błąd siedzi tylko i wyłącznie w twojej funkcji porównującej

0

To w takim razie jak powinienem napisać tę funkcję, by działała poprawnie?

1 użytkowników online, w tym zalogowanych: 0, gości: 1