Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Przypisywanie przedziałom wartości słownej - tablica
Forum PHP.pl > Forum > PHP
pawdoh
Witam mam następujący problem otóż, pobieram z bazy danych wartość będącą liczbą i teraz mam problem ponieważ potrzebuję to przypisać mam kod

  1. <?php
  2. $ranga = (
  3. range(0, 3999) => 'Początkujący',
  4. range(4000, 7999) => 'Młodzian',
  5. range(8000, 11999) => 'Doświadczony',
  6. range(12000, 20000) => 'Weteran',
  7. )
  8. ?>

następnie to przypisuje w pliku do którego załączam tą tablice, $ranga[$r[rank]]. I po tym właśnie zwraca mi błąd

Warning: Illegal offset type in ... on line 3,4,5,6

Bardzo bym prosił o pomoc gdyż jestem początkującym programistą i nie wiem za bardzo gdzie zrobiłem błąd. Pozdrawiam
kulczycki
Nie możesz ID z tablicy przypisać funkcji. Może być jedynie zmienna. Jeśli chcesz uzyskać podobny efekt to byś musiał zrobić

  1. foreach (range(0, 3999) as $number)
  2. $ranga[$number] = "Początkujący";
  3.  
  4. foreach (range(4000, 7999) as $number)
  5. $ranga[$number] = "Młodzian";
  6.  
  7. foreach (range(8000, 11999) as $number)
  8. $ranga[$number] = "Doświadczony";
  9.  
  10. foreach (range(12000, 20000) as $number)
  11. $ranga[$number] = "Weteran";



Ale to i tak jest błędne. Jeśli pobierasz X wartość z bazy to po prostu robisz
Kod
if($wartosc >= 0 && $wartosc <= 3999)
$ranga = "Początkujący";
elseif($wartosc >= 4000 && $wartosc <= 7999)
$ranga = "Młodzian";


i tak dalej.
cycofiasz
Kluczem w tablicy może być string lub liczba całkowita - w Twoim kodzie próbujesz wstawić tam rezultat działania funkcji range czyli tablicę.
Crozin
Zapisujesz sobie jedynie górną granicę i etykietę, a później sprawdzasz po kolei czy ranga użytkownika jest wystarczająco wysoka:
  1. $ranges = array(
  2. 20000 => 'Weteran',
  3. 11999 => 'Doświadczony',
  4. 7999 => 'Młodzian',
  5. 3999 => 'Początkujący'
  6. );
  7.  
  8. foreach ($ranges as $range => $label) {
  9. if ($user->getRange() < $range) {
  10. continue;
  11. }
  12.  
  13. echo $label;
  14.  
  15. break;
  16. }
pawdoh
Cytat(kulczycki @ 2.01.2011, 22:54:21 ) *
Nie możesz ID z tablicy przypisać funkcji. Może być jedynie zmienna. Jeśli chcesz uzyskać podobny efekt to byś musiał zrobić

  1. foreach (range(0, 3999) as $number)
  2. $ranga[$number] = "Początkujący";
  3.  
  4. foreach (range(4000, 7999) as $number)
  5. $ranga[$number] = "Młodzian";
  6.  
  7. foreach (range(8000, 11999) as $number)
  8. $ranga[$number] = "Doświadczony";
  9.  
  10. foreach (range(12000, 20000) as $number)
  11. $ranga[$number] = "Weteran";



Ale to i tak jest błędne. Jeśli pobierasz X wartość z bazy to po prostu robisz
Kod
if($wartosc >= 0 && $wartosc <= 3999)
$ranga = "Początkujący";
elseif($wartosc >= 4000 && $wartosc <= 7999)
$ranga = "Młodzian";


i tak dalej.


Zrobiłem jak kolega powiedział ale jeśli mam poniżej Ranga: $ranga to nic nie wyświetla popełniłem gdzieś błąd?

  1. elseif($array['ranga] > -999 && $array['ranga] < 999)
  2. $ranga = 'ranga1';
  3. elseif($array['ranga] > 1000 && $array['ranga'] < 3999)
  4. $ranga = 'ranga2';
  5. elseif($array['ranga'] > 4000 && $array['ranga'] < 7999)
  6. $ranga = 'ranga3';
  7. elseif($array['ranga'] > 8000 && $array['ranga'] < 11999)
  8. $ranga = 'ranga4';

Crozin
Cytat
[...] popełniłem gdzieś błąd?
Posłuchałeś @kulczycki, który zaproponował utworzenie tablicy złożonej z 20 000 elementów tylko po to, by sprawdzić którą z 4. etykiet przypisać użytkownikowi.
kulczycki
Cytat
Posłuchałeś @kulczycki, który zaproponował utworzenie tablicy złożonej z 20 000 elementów tylko po to, by sprawdzić którą z 4. etykiet przypisać użytkownikowi.

To wróć do góry i zobacz co zaproponowałem. Pokazałem mu tylko jak by wygładał jego przykład a nie zaproponowałem winksmiley.jpg. A rozwiązanie podałem najłatwiejsze z możliwych. Do tego twój przykład letko waldiwy jest.
thek
Ja bym nie ufał przedziałom górnym. Kulczycki ma rację co do pewnego małego babola u Ciebie Crozin. Nie reagujesz na przekroczenie zakresu maksymalnego w ostatnim wypadku dla weterana. Gdy ktoś przekroczy 20000 - nie pokaże nic.
  1. if($wartosc >= 0) {
  2. if( $wartosc > 3999 ) {
  3. if( $wartosc > 7999 ) {
  4. if( $wartosc > 7999 ) {
  5. if( $wartosc > 11999 ) {
  6. $ranga = 'Weteran';
  7. } else {
  8. $ranga = 'Doświadczony';
  9. }
  10. } else {
  11. $ranga = 'Doświadczony';
  12. }
  13. } else {
  14. $ranga = 'Młodzian';
  15. }
  16. } else {
  17. $ranga = 'Początkujący';
  18. }
  19. } else {
  20. $ranga = 'Cheater'; //jeśli wrzucasz kogoś na minus to na bank coś przeskrobał :)
  21. }
Tu masz wersję zagnieżdżona dla rang. Nie jest ładna, ale zadziała jak trzeba. Jeśli chcesz, by funkcja była dynamicznie modyfikowalna (co jakiś czas zmieniasz nazwy rang lub wartości) to stwórz funkcję, gdzie jako parametr przekazujesz tablicę z zakresami dolnymi jako klucze smile.gif Przykład masz tutaj mniej więcej...
  1. <?php
  2. $tabela_zakresow = array(
  3. 0 => 'Początkujący',
  4. 12000 => 'Weteran',
  5. 4000 => 'Doświadczony',
  6. 8000 => 'Młodzian'
  7. );
  8. //ważne są klucze i nazwy. Nie muszą być po kolei.
  9.  
  10. function ranga( $staty, $rangi ) {
  11. //sprawdzam czy mi podano tablicę rang. Jeśli nie - rzucam false, ale można też tutaj sypnąć wyjątkiem, jak kto woli.
  12. if( !is_array( $rangi ) ) {
  13. return false;
  14. }
  15. //tu sprawdzam czy mi podano liczbę postów/artykułów/czegoś usera, tu też może być wyjątek.
  16. if( !is_numeric( $staty ) ) {
  17. return false;
  18. }
  19. //sortowanie kluczy! dzięki temu nie muszę ich podawać w tablicy po kolei :) Te sortowanie załatwia mi ów problem
  20. ksort( $rangi, SORT_NUMERIC );
  21. //magia biggrin.gif
  22. end($rangi);
  23. while ( !is_null( $key = key($rangi) ) ) {
  24. if( $key > $staty ) {
  25. prev($rangi);
  26. } else {
  27. return current($rangi);
  28. }
  29. }
  30. return 'Cheater!';// To jeśli w tablicy brak rangi, a więc poniżej progu najniższego
  31. }
  32.  
  33. echo ranga( 'test', $tabela_zakresow ).'<br />';//powinno zwrócić pustkę lub false jeśli zrobisz var_dump( ranga( 'test', $tabela_zakresow ) ) :)
  34. echo ranga( -666, $tabela_zakresow ).'<br />';
  35. echo ranga( 10000, $tabela_zakresow ).'<br />';
  36. echo ranga( 4000, $tabela_zakresow ).'<br />';
  37. echo ranga( 20000, $tabela_zakresow ).'<br />';
  38. echo ranga( 500, $tabela_zakresow ).'<br />';
  39. ?>
Oczywiście "magia" to żart. Posortowaną tablicę przeszukuję od końca tak długo, aż nie dojdę do początku. Gdy jestem w pętli to porównuję wartość liczbową podaną w funkcji do aktualnego progu i jeśli klucz jest większy to lecę do elementu wcześniej w tablicy. Jeśli nie jest to prawda, to znaczy, że wpadłem we właściwy zakres i zwracam wartość dla tego klucza. Jeśli dojdę do końca i nie znajdę pasującego to wywalam rangę domyślną dla oszusta smile.gif

Zwracaj uwagę na to co zwraca funkcja ranga. jeśli dała w wyniku false to sygnalizuj błąd. Uważaj na 0... Jeśli zrobisz głupie
  1. if( ranga(0, $tabela) ) {
  2. } else {
  3. else 'error';
  4. }

to wywali Ci error jeśli będziesz zwracał jako nazwę 0, '0' lub cokolwiek innego dające się do false przekonwertować w locie. Powinno być:
  1. if( ranga(0, $tabela) !== false ) {
  2. } else {
  3. else 'error';
  4. }

Do latania po pętli mogłem też użyć foreach, co jest wygodne przy małych petlach. Ale tutaj dla celów edukacyjnych użyłem while, byś wiedział, że tak też można, a dodatkowo przy ogromnych pętlach while jest wydajniejsze. Tutaj to tylko sztuka dla sztuki i ukazanie że można. Równie dobrze możesz to foreach zastąpić, bo wątpliwe byś miał kiedykolwiek więcej niż kilka tysięcy rang winksmiley.jpg Dodatkowo jeśli masz pewność, że klucze są już podane w odpowiedniej kolejności od najmniejszej do największej to możesz usunąć z wnętrza ksort, który spowalnia całość.
Crozin
@thek: Ty to się potrafisz rozpisać.

1. Zapewne przekroczenie rangi "weteran" jest niemożliwe. Zresztą... zawsze, można zamienić znak mniejszości na większości i porównywać minimalny zakres rangi, bo jak się domyślam, ów "weteran" to maksimum.
2. Do wywalania błędów używaj wyjątków, a nie "flagi" w postaci zwrócenia false - to jest chyba jedna z bardziej "błędotwórczych" praktyk.
3. Żeby się już nikt nie czepiał:
  1. function getRange($userRange) {
  2. static $ranges = array(
  3. 'Początkujący' => array(0, 3999),
  4. 'Doświadczony' => array(4000, 7999),
  5. 'Młodzian' => array(8000, 11999),
  6. 'Weteran' => array(12000, 20000)
  7. );
  8.  
  9. foreach ($ranges as $label => $range) {
  10. if ($userRange >= $range[0] && $userRange <= $range[1]) {
  11. return $label;
  12. }
  13. }
  14.  
  15. throw new OutOfRangeException("Value '$userRange' does not match any range.");
  16. }
thek
@Crozin:
ad1) "Zresztą... zawsze, można zamienić znak mniejszości na większości i porównywać minimalny zakres rangi" -> właśnie to zaproponowałem jako lepszy pomysł. Zakres górny w przypadku liczb rosnących jest źródłem potencjalnych błędów w przyszłości.

ad2) Wyjątki także sugerowałem w komentarzach w kodzie, ale zostawiłem wybór. Poza tym dobrze napisany skrypt nie sprawi problemu. Wiesz co dostajesz na wyjściu, co dajesz na wejściu i nie martwisz się logiką biznesową wnętrza. Wyjątki są bardzo zdradliwe. Możesz mieć wszystko cacy i nagle coś sprawi, że poleci wyjątek, a wysypie Ci aplikację z powodu nie przechwyconego fatala. Ty w trakcie testowania nie musiałeś na taką sytuację trafić, ale kto wie co się stanie po pół roku na serwerze produkcyjnym? winksmiley.jpg Użycie false do sygnalizacji błędu jest w takim momencie "bezpieczne", ponieważ większość skryptów, funkcji php-owych false daje domyślnie jako sygnalizację, że coś poszło nie tak i pod tym kątem większość osób testuje returny z funkcji. Owszem, wyjątki są o wiele lepsze do tego celu i dlatego powstały, ale wymaga to przejrzenia kodu aplikacji by wiedzieć gdzie te wyjątki mogą być rzucone, a więc i gdzie je próbować łapać oraz jakiej są klasy, bo przecież wyjątki także podlegają dziedziczeniu, a czasem zależy nam na łapaniu nie wszystkich, ale tylko określonej klasy wyjątków smile.gif

ad3) Chyba lepszym wyjściem niż robić break; byłoby od razu walnięcie return. Wtedy spada konieczność sprawdzania czy userRangelabel jest nullem. Dodatkowo przesłałbym tablicę zakresów jako parametr z tej przyczyny, że wtedy funkcja staje się uniwersalną i możesz ją stosować nie tylko dla rzeczy na sztywno i potem definiować osobną do Postów, osobna do artykułów itp. Po prostu byłaby jakąś w helperze do sprawdzania czy liczba mieści się w określonym zakresie. A więc byłaby bardzo elastyczna.

PS: Popraw też static $ranges na coś innego niż weteran winksmiley.jpg
Crozin
3) Oczywiście, dobra uwaga - już poprawiłem.
2) PHP niestety nie informuje na poziomie języka o tym jakie wyjątki może dana metoda wyrzucić, ale w miarę dobrze udokumentowany już kod tak. Z return false; jest ten problem, że bardzo często nie sprawdza się czy taka właśnie wartość została zwrócona, wyjątki zaś wymuszają (w przypadku PHP nie jest to do końca prawdą) na nas obsługę tego błędu, albo przynajmniej świadome zignorowanie go.
thek
ad3) nadal przypisanie nulla przed foreachem jest zbędne. Ta linia nie robi kompletnie nic i nigdzie nie jest wykorzystywana. No i nadal jako indeksy tablicy statycznej miałeś samych Weteranów, ale poprawiłem Ci to już sam.

ad2) No niestetynie informuje i to jest moim zdaniem pewien problem. Nie zagląda się bowiem nieraz głębiej niż ostatni poziom dziedziczenia. Potem nam z powietrza się we wnuczku pojawia exception z powietrza bo dziadek go implementował lub tatuś biggrin.gif Ja tak miałem w Swift mailerze, gdy mi się skrypt mailingu wysypał i jak się okazało wyjątkiem sypnęła jakaś klasa transportu, która robiła za podstawę dla innych winksmiley.jpg Nie zauważyłbym tego, gdyby nie przypadkowa uwaga kumpla, że mailing się nie dostarczył mu na konto testowe. Normalnie caly mailing idzie cronem, z możliwościa wywołania poprzez http, a wyjście normalnie sypie się na /dev/null winksmiley.jpg Dopiero gdy skierowałem wyjście na przeglądarkę i zobaczyłem nieobsłużony wyjątek klasy transportowej przy errorze 500 nagłówka podczas wysyłki to mi kopara opadła winksmiley.jpg A dodam, że zablokowany skrypt nie przeskakuje błędnego adresu, tylko próbuje do oporu. Cały mailing więc był zablokowany.
Crozin
3) Tak to jest jak się kod kopiuje i poprawia na szybko. winksmiley.jpg Co do rang - uznałem, że autor nie jest skończonym idiotą i nie skopiuje-wklei tego bez rzucenia okiem. No i nie wyrównałeś ładnie elementów tablicy. winksmiley.jpg

2) Ale dokładnie to samo przytrafi Ci się gdy gdzieś wystąpi błąd, zostanie zwrócone false, a Ty tego nie obsłużysz - takie sytuacje zdarzają się znacznie częściej niż w przypadku wyjątków. Co do nieprzechwyconych wyjątków - m.in. dlatego dobrze jest cały kod objąć blokiem try-catch(Exception), ewentualnie użyć exception handlera by nieobsłużone wyjątki (do czego nigdy nie powinno dojść) gdzieś zapisać, poinformować o ich wystąpieniu.
kulczycki
1. co do exception, po co on ?. Od tej funkcji nie zależy działanie całego serwisu, a tylko przypisanie rangi danemu użytkownikowi. Jeśli nie ma x przedziału to po prostu ustawia najbardziej bliską, a jeśli dalej nie może ustawić to wywala pusty albo "No range" - to przecież tylko tekst o range.

2. Cozin twoja pierwsza wersja była dobra ale trzeba lekko dopracować. I ta ostatnia to samo. Bo jeśli ostatnia ranga się gdzieś kończy to przecież nie odbierzesz rangi użytkownikowi tylko dlatego że ma więcej punktów winksmiley.jpg

Może takie niechlujstwo ale trudno:

  1. function getRange($userRange) {
  2. static $ranges = array(
  3. 'Początkujący' => array(0, 3999),
  4. 'Doświadczony' => array(4000, 7999),
  5. 'Młodzian' => array(8000, 11999),
  6. 'Weteran' => array(12000, 20000)
  7. );
  8.  
  9. $max = count($ranges);
  10. $i = 0;
  11.  
  12. foreach ($ranges as $label => $range) {
  13. if($i == $max-1)
  14. if($userRange >= $range[1])
  15. return $label;
  16.  
  17. if ($userRange >= $range[0] && $userRange <= $range[1]) {
  18. return $label;
  19. }
  20.  
  21. $i++;
  22. }
  23.  
  24. return "No range";
  25. }
  26.  
  27. echo getRange(50);
  28. echo getRange(5000000000000000);
Crozin
  1. Oczywiście możesz zwrócić wartość domyślną w momencie kiedy nie da się określić innej, ale gdyby brak możliwości określenia rangi miał skutkować błędem to powinieneś wykorzystać wyjątek, bo nadaje się on do tego dobrze. I nie musi to być jakiś błąd krytyczny.
  2. Zwróć tylko uwagę na jedno: Twoje rozwiązanie jest lekko zdradzieckie, bo działanie kodu jest lekko nielogiczne. Najpierw jasno określasz konkretne przedziały dla danych rang, a później je ignorujesz. Tutaj nie jest to jakoś specjalnie groźne, bo jest to bardzo prosty przykład, ale w bardziej skomplikowanych przypadkach może to prowadzić do pewnych nieścisłości, zaciemnienia sposobu działania kodu, błędów itp. Innymi słowy: jest to zła praktyka.

    Tutaj lepszym pomysłem wydaje się bezpośrednie przewidzenie sytuacji, gdzie dana ranga ma dolny bądź górny przedział otwarty. Przez otwarty mam na myśli, plus minus nieskończoność, którą mógłbyś sygnalizować przy użyciu wartości NULL:
    1. static $ranges = array(
    2. 'Początkujący' => array(null, 3999),
    3. 'Doświadczony' => array(4000, 7999),
    4. 'Młodzian' => array(8000, 11999),
    5. 'Weteran' => array(12000, null)
    6. );
    Oczywiście późniejszy kod nieco się zmienia.

    Może Ci się to wydawać niepotrzebnym komplikowaniem kodu, ale w rzeczywistości sprawia to, że jego działanie jest łatwiejsze do zrozumienia.
kulczycki
Bez bicia przyznaje racje winksmiley.jpg
thek
Ja, właśnie z racji tego, że musimy uważać na zakresy uważam, że tak naprawdę konieczne do poznania są tylko dolne granice. Weteran będzie nim, niezależnie czy posiada 15tysięcy czy 150tysięcy punktów. Stąd dla niego zakres górny jest zbyteczny i sygnalizowanie błędu mija się z celem. Za to istotne jest zejście poniżej wartości 0, która jest wartością graniczną i w normalnej sytuacji nie powinna wystąpić. Może miec to miejsce w przypadku przekręcenia licznika w bazie. A takie coś już jest na pewno sytuacją nienormalną i powinno być oznaczone jako wyjątek. W końcu własnie po to ten mechanizm powstał. Niektórzy używają wyjątków do zwracania wartości, ale to moim zdaniem zła praktyka. Nie po to powstały elementy w stylu rethrow, który pozwala obsłużyć wyjatek i rzucić go ponownie w kod (ale to już nie w php) winksmiley.jpg
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.