2010-10-27 13 views
12

Ho un array che rispecchia le percentuali di sconto a seconda del numero degli articoli ordinati:Picking il valore più vicino da una matrice che riflette gamme

$rebates = array(
    1 => 0, 
    3 => 10, 
    5 => 25, 
    10 => 35) 

che significa che per uno o due elementi, si ottiene nessun abbuono; per 3+ articoli ottieni il 10%, per 5+ articoli 20%, per 10+ 35% e così via.

Esiste un elegante, un modo a una riga per ottenere la percentuale di sconto corretta per un numero arbitrario di articoli, ad esempio 7?

Ovviamente, questo può essere risolto utilizzando un ciclo semplice: non è quello che sto cercando. Mi interessa sapere se esiste un array principale o un'altra funzione che non conosco che possa farlo in modo più elegante.

Ho intenzione di assegnare la risposta accettata a una taglia di 200, ma a quanto pare, devo aspettare 24 ore fino a quando posso farlo. La domanda è risolta.

+3

si sa che è inutile chiedere one-liners, quando tu non specificare la massima della linea di lunghezza;) – Gordon

+0

chiavi non sono in un modello? Quindi non possiamo fare una funzione – nerkn

+0

@Gordon: prima del codice golf. – BoltClock

risposta

16

eccone un altro, ancora una volta non Insomma.

$percent = $rebates[max(array_intersect(array_keys($rebates),range(0,$items)))]; 

L'idea è sostanzialmente quella di ottenere il tasto più alto (max) che è da qualche parte tra 0 e $items.

+0

+1 pulito e pratico –

+0

Lo accetto perché funziona in modo non distruttivo, è l'approccio più breve e non ha bisogno di elementi globali/chiusure. Grazie! –

+0

+1 relativamente breve e funzionale – Thariama

1

Non esiste tale funzione principale!

1

migliore che posso gestire finora:

$testValue = 7; 
array_walk($rebates, function($value, $key, &$test) { if ($key > $test[0]) unset($test[1][$key]); } array($testValue,&$rebates)); 

Utilizza un piccolo capriccio brutta di passaggio per riferimento, e le strisce fuori una voce nella matrice $ sconti dove la chiave è numericamente maggiore di $ testvalue ... sfortunatamente, lascia comunque le voci con la chiave più bassa, quindi un array_pop() sarebbe necessario per ottenere il valore corretto. Si noti che riduce attivamente le voci nella matrice di sconti $ originale.

Forse qualcuno può costruire su questo per scartare le voci in basso nella matrice.

Non avere 5.3.3 a disposizione al momento, quindi non testato utilizzando una funzione anonima, ma funziona (per quanto funziona) quando si utilizza una funzione di callback standard.

EDIT

Basandosi su mio precedente one-liner, l'aggiunta di una seconda linea (quindi probabilmente non dovrebbe contare):

$testValue = 7; 
array_walk($rebates, function($value, $key, &$test) { if ($key > $test[0]) unset($test[1][$key]); } array($testValue,&$rebates)); 
array_walk(array_reverse($rebates,true), function($value, $key, &$test) { if ($key < $test[0]) unset($test[1][$key]); } array(array_pop(array_keys($rebates)),&$rebates)); 

Ora risultati nella matrice $ sconti contiene solo un singolo elemento, essendo la chiave punto di rottura più alta dall'array $ sconto originale che è una chiave inferiore a $ testValue.

+0

+1 Approccio interessante! –

2

Questo potrebbe funzionare senza modificare l'array di sconti.

Ma la matrice deve essere costruito in un altro modo per far funzionare tutto questo

$rebates = array(
    3 => 0,  //Every number below this will get this rebate 
    5 => 10, 
    10 => 25, 
    1000 => 35); //Arbitrary large numer to catch all 

$count = $_REQUEST["count"]; 

$rv = $rebates[array_shift(array_filter(array_keys($rebates), function ($v) {global $count; return $v > $count;}))]; 

echo $rv; 

testcase di lavoro, basta cambiare contano in url

http://empirium.dnet.nu/arraytest.php?count=5
http://empirium.dnet.nu/arraytest.php?count=10

+0

Molto bello, grazie. Sto accettando l'approccio di @ salathe perché è un po 'più breve e funziona senza una chiusura globale, ma funzionerà anche bene. –

+0

Di causa, la sua soluzione è più elegante :-) –

5

Penso che le soluzioni di una linea sopra non siano davvero eleganti o leggibili. Quindi perché non usare qualcosa che possa essere compreso da qualcuno a prima vista?

$items = NUM_OF_ITEMS; 
$rabate = 0; 
foreach ($rabates as $rItems => $rRabate) { 
    if ($rItems > $items) break; 
    $rabate = $rRabate; 
} 

Questo ha ovviamente bisogno di un array ordinato, ma almeno nel tuo esempio questo è dato;)

Va bene, lo so, non si vuole la soluzione con il semplice ciclo. Ma che dire di questo:

while (!isset($rabates[$items])) { 
    --$items; 
} 
$rabate = $rabates[$items]; 

Ancora piuttosto semplice, ma un po 'più breve. Possiamo fare anche più breve?

for (; !isset($rabates[$items]); --$items); 
$rabate = $rabates[$items]; 

Ci stiamo già avvicinando a una linea. Quindi facciamo un po 'di barare:

for (; !isset($rabates[$items]) || 0 > $rabate = $rabates[$items]; --$items); 

Questo è più breve di tutti gli approcci in altre risposte. Ha solo uno svantaggio: cambia il valore di $items che potrebbe essere necessario in seguito. Così abbiamo potuto fare:

for ($i = $items; !isset($rabates[$i]) || 0 > $rabate = $rabates[$i]; --$i); 

Ecco di nuovo un carattere meno e continuiamo $items.

Anche se penso che le ultime due versioni siano già troppo hacky. Meglio restare con questo, in quanto è sia a breve e comprensibile:

for ($i = $items; !isset($rabates[$i]); --$i); 
$rabate = $rabates[$i]; 
Problemi correlati