Autor | Zpráva | ||
---|---|---|---|
mackopu Profil |
#1 · Zasláno: 9. 1. 2016, 14:14:14
Mám pole např. o 2150 prvcích. A potřebuji ho zredukovat na max. 1000 prvků. Ne prvních nebo posledních nebo náhodných, ale prostě tak, aby zůstal každý n-tý prvek.
Jde to nějak elegantně? |
||
Dan Charousek Profil |
#2 · Zasláno: 9. 1. 2016, 14:27:12
mackopu:
Můžeš si původní pole projet foreachem a pukládat si zvolené hodnoty do nového pole: $n = 5; // každý pátý prvek $i = 0; $newArray = []; foreach($oldArray as $key => $value) { $i++; if($i%$n != 0) continue; $newArray[$key] = $value; } |
||
juriad Profil |
#3 · Zasláno: 9. 1. 2016, 14:33:12
Pokud se jedná o pole a nikoli o asociativní pole, můžeš postupovat takto:
function everyNth($array, $nth, $mod = 0) { $new = array(); for ($i = $mod; $i <= count($array); $i += $nth) { $new[] = $array[$i]; } return $new; } Dan Charousek: $i++ patří až na konec smyčky. Ale máš tam tu nevhodnou podmínku s continue, takže by šlo: if ($i++ % $n != 0) .
|
||
Dan Charousek Profil |
#4 · Zasláno: 9. 1. 2016, 14:35:21
juriad:
Poku by bylo $i++ až na konci smyčky, tak se v případě splněné splněné podmínky, jak jsi uvedl, neprovede, ale hlavně vzalo by to i první prvek, což předpokládám, není žádané.
|
||
juriad Profil |
Dan Charousek:
Ono záleží na definici n-tý. Já za každý pátý považuji prvky s indexem 0, 5, 10... Ale vím, že někteří nebudou souhlasit, proto je tam v mé funkci ten paramer $mod. Aneb, pokud má pole 6 prvků a ty chceš každý pátý, očekáváš, že nové pole bude obsahovat jeden nebo dva prvky? |
||
mackopu Profil |
#6 · Zasláno: 9. 1. 2016, 14:51:05
juriad:
Očekávám, že bez ohledu na počet prvků původního pole bude mít nové pole 1000 prvků, samozřejmě pokud jich má původní pole více. A bylo by fajn, kdyby první prvek nového pole byl totožný s prvním prvkem pole původního. |
||
Keeehi Profil |
V rámci toho jednoho pole
$array = [1,2,3,4,5,6,7,8]; $count = count($array); $limit = 3; // pokud chceš max 1000 prvků, přiřaď sem 1000 $mod = ceil($count/$limit); for ($i = 0 ; $i < $count ; $i++) { if($i % $mod != 0) { unset($array[$i]); } } |
||
juriad Profil |
mackopu:
Co vlastně chceš? Chceš každý n-tý prvek? Pak jich ale nemusí být 1000. Chceš z původního pole vybrat rovnoměrně 1000 reprezentantů? Pak od sebe nebudou vzdálení stejně. Chceš vybrat každý n-tý, oříznout pole na 1000, pro takové n, aby se zahodilo nejméně prvků? Musí nové pole mít 1000 prvků, nebo může mít méně? Co když původní pole mělo 1999 prvků? Které bys chtěl v takovém případě? |
||
mackopu Profil |
#9 · Zasláno: 9. 1. 2016, 14:59:40
juriad:
Ano, chci tu druhou tebou uvedenou možnost. |
||
Dan Charousek Profil |
#10 · Zasláno: 9. 1. 2016, 15:25:42
mackopu:
<?php function reduceArray($array, $reduceTo) { $mod = count($array)/$reduceTo; $keys = array_keys($array); $newArray = []; for($i = 0; $i < count($keys); $i+= $mod) { $newArray[$keys[ceil($i)]] = $array[$keys[ceil($i)]]; } return $newArray; } $arr = range(1, 2150); $new = reduceArray($arr, 1000); |
||
mackopu Profil |
#11 · Zasláno: 9. 1. 2016, 15:43:10
Všem děkuji, pomohli jste mi a problém je vyřešen.
|
||
Keeehi Profil |
#12 · Zasláno: 9. 1. 2016, 15:47:04
Dan Charousek:
Má to problém. Pro reduceArray($arr, 2149); to šahá mimo pole.
|
||
Dan Charousek Profil |
#13 · Zasláno: 9. 1. 2016, 15:54:27
Keeehi:
Pravda, for($i = 0; $i < count($keys) - floor($mod); $i+= $mod) { $newArray[$keys[ceil($i)]] = $array[$keys[ceil($i)]]; } by to mělo opravit. |
||
Keeehi Profil |
#14 · Zasláno: 9. 1. 2016, 16:20:33
Výborně, a teď ještě toto:
$arr = range(0, 101); $new = reduceArray($arr, 4); // 0, 26, 51, 77 Ještě jeden problém vidím $i+= $mod , v mod je desetinné číslo, které se velmi často musí zaokrouhlit kvůli paměti v počítači. Takže je lehce nepřesné. No a protože se to cyklicky sčítá, tato nepřesnost se kumuluje a roste. U 1000 se to nejspíše neprojeví, ale u vyšších řádů by mohlo.
Proto bych navrhoval <?php function reduceArray(&$array, $reduceTo) { $count = count($array)-1; $mod = $count/($reduceTo-1); $cnt = 0; for ($i = 0 ; $i < $count ; $i++) { if($i < round($mod*$cnt)) { unset($array[$i]); } else { $cnt++; } } } $array = range(0,100); reduceArray($array, 4); ?> |
||
Alphard Profil |
Vy jste hrozní kouzelníci :-) Můj pohled na věc je takový, že v prvním kroku si vytvořím funkci linspace (známá z Pythonu, Matlabu, ...) a v druhém kroku ji použiji.
Zmíněná funkce linspace by implementačně byla už velmi podobná řešení [#14] Keeehi, ale očekával bych, že poslední prvek pole doplní tak, aby přesně odpovídal zadání (tím rozhodně nemyslím násobení desetinným $mod). Cvičně si to programovat nebudu, příklad implementace v pythonu je např. na github.com/numpy/numpy/blob/v1.10.0/numpy/core/function_base.py#L9-L120, zdůraznil bych řádky 114-115, které řeší poslední prvek (v této implementaci je škoda toho přetypování na float, což vynucuje numpy, ale v PHP by tento problém neyl). Když tuto funkci budu mít hotovou, zbyde triviální část úkolu function reduceArray($input, $outputSize) { $output = []; foreach (linspace(0, count($input)-1, $outputSize) as $key) { $output[] = $input[round($key)]; // změnil jsem floor na round } return $output; } Výsledkem bude [0, 34, 67, 101] , což bych očekával.
|
||
Časová prodleva: 9 let
|
0