Page 1 sur 1

comparaison tranche valeur

Posté : 13 déc. 2007, 08:53
par Floo
Bonjour,
Je souhaiterai déterminer la valeur d'une variable en la comparant à une tranche de valeur.
Pour être plus clair il s'agit de déterminer un prix de frais de port en fonction du poids d'un colis.
Pour cela je dispose d'une grille tarifaire de ce type :
de 0 à 2000g > 1€ de frais de port
de 2000g à 5000g > 2€ ...etc

J'aimerai trouver une méthode optimisée pour effectué ce calcul. J'ai bien pensé à faire quelque chose de ce genre :
if ($poids > 0 && $poids <= 2000) 
{
$fraisPort = 1;
}

if ($poids > 2000 && $poids <= 5000) 
{
$fraisPort = 2;
}
..Etc
Mais je trouve ça un peu basique surtout que ma grille compte 9 références, ça fait donc beaucoup de if pour par grand chose.
Je suis sûr qu'il y a beaucoup plus simple...non ?

Posté : 13 déc. 2007, 09:43
par naholyr
Tu peux faire quelque chose de très évolutif en utilisant un tableau. De plus tu as certains tests inutiles :
- le poids sera toujours positif, sinon c'est qu'il y a un bug en amont, et c'est en amont qu'il faut le régler. Le test "$poids > 0" est donc inutile.
- le poids, s'il n'est pas inférieur ou égale à 2000, est donc forcément strictement supérieur à 2000. Si tu testes d'abord "$poids <= 2000" il n'est pas nécessaire de tester ensuite "$poids > 2000" si tu as simplement placé ton test dans un "elseif".


Voici comment je procèderais (largement inspiré du traitement des tarifs dégressifs dans une boutique qu'on a développée récemment) :
// poids => frais de port, par ordre de poids décroissant
$poids_fraisPort = array(
  0 => 1,
  2000 => 2,
  5000 => 3,
);

foreach ($poids_fraisPort as $tranche_bas => $fraisPort) {
  if ($poids <= $tranche_bas) {
    break;
  }
}
// $fraisPort = frais de port pour la tranche de poids

Posté : 13 déc. 2007, 10:21
par Floo
Génial c'est exactement ce que je voulais ! merçi :wink:
Par contre c'est pas plutôt $tranche_haut pour la variable ? ou c'est moi qui pige pas complètement la logique de fonctionnement ?

Posté : 13 déc. 2007, 11:21
par Ryle
Il y a effectivement un soucis avec le code de naholyr car son tableau ne devrait pas commencer à zéro (c'est d'ailleurs ce qu'il indique dans son commentaire : poids décroissants ;))
Qu'on l'appelle tranche basse ou haute ne change pas grand chose, pour nous c'est en fait la tranche "en cours" ;)

En effet, avec son code, si ton montant est de 500, alors tu vas te retrouver dans la tranche 2 :
- 500 est supérieur à 0 (=> tranche 1), donc on passe à l'itération suivante.
- 500 est inférieur à 2000 (=> tranche 2), on sort de la boucle, mais pas avec la bonne tranche :)

Il te faut donc inverser les éléments du tableau pour les trier par ordre de poids décroissant (et changer le sens de la comparaison)
// poids => frais de port, par ordre de poids décroissant 
$poids_fraisPort = array( 
  5000 => 3,
  2000 => 2, 
  0 => 1
); 

foreach ($poids_fraisPort as $tranche => $fraisPort) { 
  if ($poids > $tranche) { // si le poids est supérieur à la tranche en cours
    break; // on arrête la boucle et on en sort avec les frais associés à la dernière tranche parcourue
  } 
} 
// $fraisPort = frais de port pour la tranche de poids

Posté : 13 déc. 2007, 12:06
par naholyr
mes commentaires et les nommages de variables ne correspondent pas en effet ^^ j'ai mélangé joyeusement deux méthodes en essayant de me souvenir de comment on avait réalisé le notre. En revanche ça reste juste il suffit de définir correctement le tableau pour coller exactement à l'exemple que tu as donné :
array(
  2000 => 1,
  5000 => 2,
  10000 => 3,
  // etc...
);
En effet c'est plutôt $tranche_haut que $tranche_bas, mais c'est super ça signifie que tu as parfaitement compris le déroulement de l'algo ;)

Prenons un exemple déroulement Dans ton cas la tranche bas sera plutôt 2001 et 5001, ou un test avec "strictement inférieur". Vérifions avec un exemple à 2001 (qui devrait donc donner 2 en frais de port) :
- Premier passage : $tranche_haut = 2000, $fraisPort = 1. (2001 <= $tranche_haut) est faux, on continue
- Second passage : $tranche_haut = 5000, $fraisPort = 2. (2001 <= $tranche_haut) est vrai, on arrête et la valeur de $fraisPort est correctement définie à 2.

Cas limite : $poids = 15000
On va parcourir tout le tableau sans jamais s'arrêter, donc $fraisPort vaudra simplement al dernière valeur : 3, c'est bien le résultat attendu.

Le code fonctionne bien, simplement qu'il était camouflé derrière des commentaires et des noms de variable qui ne voulaient rien dire :lol:

Le voici corrigé :
// poids => frais de port, par ordre de poids croissant
$poids_fraisPort = array(
  2000 => 1,
  5000 => 2,
  // etc...
);

foreach ($poids_fraisPort as $tranche_haut => $fraisPort) {
  if ($poids <= $tranche_haut) {
    break;
  }
}
// $fraisPort = frais de port pour la tranche de poids
La version de Ryle est l'exact opposé, et fonctionne donc parfaitement également, on parcourt simplement dans un sens ou dans l'autre : tu peux donc choisir le sens en fonction du type de données que tu auras. Si tu as plutôt des données dans les tranches hautes il vaut mieux prendre le tableau décroissant, si tu as plutôt des données dans les tranches basses il vaut mieux prendre le tableau croissant.
Ainsi tu pourras économiser au moins un ou deux passages dans ta boucle par exécution du script, et économiser 4e-8 ms !! OK, je suis parti en live là.

Posté : 13 déc. 2007, 12:41
par Floo
Merci pour tous ces détails et explications, vous êtes au top !
PS : vous pouvez passer le sujet en résolu pour moi ? j'étais pas inscrit :wink: dsl.