How to implode a generator? - php

I have a PHP generator which generates some $key => $value.
Is there an "easy" way to implode the values (by passing the generator name)? Or to transform it to an array?
I can do this with a couple of lines of code, but are there some builtins functions to accomplish this?

You can use iterator_to_array function for the same, have a look on below example:
function gen_one_to_three() {
for ($i = 1; $i <= 3; $i++) {
// Note that $i is preserved between yields.
yield $i;
}
}
$generator = gen_one_to_three();
$array = iterator_to_array($generator);
print_r($array);
Output
Array
(
[0] => 1
[1] => 2
[2] => 3
)

One has to keep in mind that
A generator is simply a function that returns an iterator.
For instance:
function digits() {
for ($i = 0; $i < 10; $i++) {
yield $i;
}
}
digits is a generator, digits() is an iterator.
Hence one should search for "iterator to array" functions, to find iterator_to_array (suggested by Chetan Ameta )

Related

How to output different values from an array

I have a problem with my code. what I want to do is to delete an item from the array when it has been called meaning that, I want every output to be different. I want to use it to rotate proxy and there are over 150 proxies in the array. Here's an example of my code.
for ( $i = 1; $i < 2; $i++ )
{
// If the array_history is empty, re-populate it.
if (empty($array_history))
$array_history = $array;
// Select a random key.
$key = array_rand($array_history, 1);
// Save the record in $selected.
$selected = $array_history[$key];
// Remove the key/pair from the array.
unset($array_history[$key]);
// Echo the selected value.
echo $selected;
}
How can I do this or is a for loop not suitable for this? thanks in advance.
What you want to do is spread access over 150 proxies. In this case, it is not really necessary to do it randomly. You can just go through the array.
<?php
$array = [0, 1, 2, 3, 4, 5, 6];
for ( $i = 1; $i < 20; $i++ )
{
echo getNext($array) . '<br>';
}
function getNext (&$array) {
$e = next($array); // Every time next element is selected. Each output is different.
if ($e)
return $e;
else
return reset($array);
}
?>
This seems like a good application for a generator. This one takes an array of proxy addresses and loops over the array in random order, shuffling the array each time it starts the loop again.
function get_proxy($proxies) {
$i = 0;
$len = count($proxies);
while (true) {
if ($i == 0) shuffle($proxies);
yield $proxies[$i];
$i = ($i + 1) % $len;
}
}
To use this, you would do something like this:
$proxies = array('10.0.0.4', '192.168.0.1', '10.1.0.1');
$i = 0;
foreach (get_proxy($proxies) as $proxy) {
echo "$proxy\n";
$i++;
// stop otherwise infinite loop
if ($i == 9) break;
}
Note that since the generator has an infinite loop in it, the external foreach loop will also be infinite, and so needs a way to break out (I've used a simple counter in this case).
Sample output for the above code:
10.1.0.1
10.0.0.4
192.168.0.1
192.168.0.1
10.1.0.1
10.0.0.4
10.1.0.1
192.168.0.1
10.0.0.4
Demo on 3v4l.org
If a generator doesn't suit your code structure, you could use a function with static variables to return a new proxy on each call:
$proxies = array('10.0.0.4', '192.168.0.1', '10.1.0.1');
function get_proxy($proxies) {
static $i = 0, $keys;
if (!isset($keys)) $keys = array_keys($proxies);
if ($i == 0) shuffle($keys);
$proxy = $proxies[$keys[$i]];
$i = ($i + 1) % count($keys);
return $proxy;
}
for ($i= 0; $i < 9; $i++) {
echo get_proxy($proxies) . "\n";
}
Sample output for this code:
10.1.0.1
10.0.0.4
192.168.0.1
192.168.0.1
10.1.0.1
10.0.0.4
10.0.0.4
192.168.0.1
10.1.0.1
Demo on 3v4l.org
When you define an array in php such as
<?php
$alphabet = array(a, b, c)
?>
Your trying to look for the elements in the array. The element list always starts at a count of 0. So to call individual elements count from left to right starting at 0.
<?php
#a
echo $alphabet[0];
#b
echo $alphabet[1];
#c
echo $alphabet[2];
?>
The above section should yield a result of abc because there are no breaks.
For loops are really handy for going through the entire array and running checks, error analysis or even math as examples.
I've changed tho cede slightly, to me it reads as code that will take a selection of items, pick one at random, discard the item, and then pick again until none are left. If there are none left, copy the original array and start again.
Currently your code loops once. I've extended the loop to 4 iteratons here. And upon each random selection, am storing that random key in a history array. You can then refer to that history later in your code.
<?php
$array =
[
'beatle' => 'John',
'stone' => 'Mick',
'floyd' => 'Syd'
];
for ($history = [], $i = 1; $i < 5; $i++)
{
if (empty($buffer))
$buffer = $array;
$key = array_rand($buffer, 1);
$history[] = $key;
echo $buffer[$key], "\n";
unset($buffer[$key]);
}
var_export($history);
Example output:
Syd
Mick
John
Syd
array (
0 => 'floyd',
1 => 'stone',
2 => 'beatle',
3 => 'floyd',
)
Referring to the history array above:
echo $array[$history[3]];
Would output Syd.
Adapting and encapsulating the above in a class. This will do the same (but not store the history of picks), taking an item from an array at random and remove until there are no more items in the array, and then replenish the list and so on:
class RandomProxies
{
const PROXIES =
[
'proxy1' => 'foo.example.com',
'proxy2' => 'bar.example.com',
'proxy3' => 'baz.example.com',
];
private $buffer;
public function getProxy() {
if (empty($this->buffer))
$this->buffer = self::PROXIES;
$key = array_rand($this->buffer);
$proxy = self::PROXIES[$key];
unset($this->buffer[$key]);
return $proxy;
}
}
$randomiser = new RandomProxies;
foreach(range(1,4) as $n) {
echo $randomiser->getProxy(), "\n";
}
Example output:
foo.example.com
baz.example.com
bar.example.com
foo.example.com

Count delimiter(s) of a string [duplicate]

What is the most efficient way to count all the occurrences of a specific character in a PHP string?
use this:
echo substr_count("abca", "a"); // will echo 2
Can you not feed the character to preg_match_all?
Not sure what kind of a response you're looking for, but here's a function that might do it:
function findChar($c, $str) {
indexes = array();
for($i=0; $i<strlen($str); $i++) {
if ($str{$i}==$c) $indexes[] = $i;
}
return $indexes;
}
Pass it the character you're looking for and the string you want to look:
$mystring = "She shells out C# code on the sea shore";
$mychar = "s";
$myindexes = $findChar($mychar, $mystring);
print_r($myindexes);
It should give you something like
Array (
[0] => 0
[1] => 4
[2] => 9
[3] => 31
[4] => 35
)
or something...
If you are going to be repeatedly checking the same string, it'd be smart to have some sort of trie or even assoc array for it otherwise, the straightforward way to do it is...
for($i = 0; $i < strlen($s); $i++)
if($s[i] == $c)
echo "{$s[i]} at position $i";

Remove Array value

[0] => LR-153-TKW
[1] => Klaten
[2] => Rectangular
[3] => 12x135x97
I have an array looking like this. and I want to completely remove 12x135x97 to the mother array so how would i do this?
You can use unset($arr[3]);. It will delete that array index. Whenever you want to delete an array value, you can use PHP unset() method.
As you were asked into your comment:
basically i just want to remove all index that have "X**X" this pattern digit 'x' digit
Here is the code that you can use:
$arr = array("LR-153-TKW", "Klaten", "Rectangular", "12x135x97", "xxxx");
$pattern_matched_array = preg_grep("/^[0-9]+x[0-9]+x[0-9]*/", $arr);
if(count($pattern_matched_array) > 0)
{
foreach($pattern_matched_array as $key => $value)
{
unset($arr[$key]);
}
}
print_r($arr);
PHP has unset() function. You can use it for deleting a variable or index of array.
unset($your_var[3]);
See http://php.net/manual/en/function.unset.php
You have many options:
if you know the array key then you can do this
unset($arrayName[3]);
or if it's always at the end of your array
array_pop($arrayName);
this will remove the last value out of your array
Use unset, to find it you can do this:
for($i = 0; $i < count($array); $i++){
if($i == "12x135x97"){
unset($array[i]);
break;
}
}
Unless you know the key, in which case you can do:
unset($array[3]);
its not the most time efficient if you array is thousands of items long, but for this job it will suffice.
To turn it into a method, would make for better coding.
function removeItem($item){
for($i = 0; $i < count($array); $i++){
if($i == $item){
unset($array[i]);
break;
}
}
return $array;
}
and call it like:
removeItem("12x135x97");

PHP for loop within for loop. Trying to sum multiple array columns to get combinations [duplicate]

This question already has answers here:
How to sum all column values in multi-dimensional array?
(20 answers)
Closed 1 year ago.
i am trying to loop through a two-dimensional array and take a sum of the combinations of the columns automatically.
Suppose i have an array called $a with 4 columns:0,1,2,3,
$a=array();
$a[0][0]=1;
$a[0][1]=3;
$a[0][2]=5;
$a[1][0]=10;
$a[1][1]=2;
$a[1][2]=5;
$a[1][3]=7;
$a[2][0]=9;
$a[2][1]=8;
$a[2][2]=9;
$a[2][3]=8;
$a[3][0]=9;
$a[3][1]=8;
$a[3][2]=9;
$a[3][3]=8;
$a[3][4]=1;
And i am trying to sum over all of the combinations of the columns like sum(0,0;1,0;2;0,3;0) etc using this code
for($i=0;$i<count($a[0]);$i++){
for($l=0;$l<count($a[1]);$l++){
for($s=0;$s<count($a[2]);$s++){
for($m=0;$m<count($a[3]);$m++){
echo $sum[]= $a[0][$i]+$a[1][$l]+$a[2][$s]+$a[3][$m];
echo $sum;
echo "<br>";
}
}
}
}
?>
And the code works, the problem is that i am doing these for loops manually, there must be some way in which i can simplify this by somehow inserting the count of the number of columns?
I tried something like
$numberofcolumns=4;
for($n=0;$n<$numberofcolumns;$n++){
for($i=0;$i<count($a[$n]);$i++){
for($m=0;$m<count($a[$n+1]);$m++){
echo $sums[]= $a[$n][$i]+$a[$n+1][$m];
}
}
}
but that doesn't work, there must be some way to simplify the for loops so that i don't have to manually type in the for loops each column
anybody have a clue?
You can use recursion, or just straight nested loops for this, but when working with combinations or permutations, the total number of possibilities can explode and become a huge number, consuming lots of memory to the point where you just cant run the code. Using an iterator is a nice way to trade cpu efficiency for memory efficiency. Here's an iterator I wrote.
class CartesianProductIterator implements Iterator {
protected $iterators;
function __construct(array $iters) {
$this->iterators = $iters;
}
function rewind() {
foreach ($this->iterators as $it) {
$it->rewind();
}
}
function current() {
$values = array();
foreach ($this->iterators as $it) {
$values[] = $it->current();
}
return $values;
}
function key() {
return null;
}
function next() {
/*
loop them in reverse, but exclude first
why? example, odometer: 55199
you always check the rightmost digit first to see if incrementing it would roll it over and need to be "rewound" to 0,
which causes the digit to the left to increase as well, which may also cause it to roll over as well, and so on...
looping in reverse operates from right column to the left.
we dont rewind the first column because if the leftmost column is on its last element and needs to roll over
then this iterator has reached its end, and so rewind() needs to be explicitly called
*/
for ($i = count($this->iterators) - 1; $i > 0; --$i) {
$it = $this->iterators[$i];
$it->next();
if ($it->valid()) {
// were done advancing because we found a column that didnt roll over
return;
} else {
$it->rewind();
}
}
//if execution reached here, then all of the columns have rolled over, so we must attempt to roll over the left most column
$this->iterators[0]->next();
}
function valid() {
return $this->iterators[0]->valid();
}
}
Then use it as
$iterators = array();
foreach ($a as $columnNumber => $values) {
$iterators[] = new ArrayIterator($values);
}
foreach (new CartesianProductIterator($iterators) as $combo) {
// combo has 1 value from each of the ArrayIterators we instantiated
printf("summing %s = %d\n", join('+', $combo), array_sum($combo));
}
heres a demo http://codepad.org/UasdgvWf
You can use RecursiveIteratorIterator
Try
$a = array ();
$a [0] [0] = 1;
$a [0] [1] = 3;
$a [0] [2] = 5;
$a [1] [0] = 10;
$a [1] [1] = 2;
$a [1] [2] = 5;
$a [1] [3] = 7;
$a [2] [0] = 9;
$a [2] [1] = 8;
$a [2] [2] = 9;
$a [2] [3] = 8;
$a [3] [0] = 9;
$a [3] [1] = 8;
$a [3] [2] = 9;
$a [3] [3] = 8;
$a [3] [4] = 1;
$sum = 0;
$array = new RecursiveIteratorIterator ( new RecursiveArrayIterator ( $a ) );
foreach ( $array as $key => $value ) {
$sum += $value;
}
echo $sum;
Output
102
Use $array = new RecursiveIteratorIterator ( new RecursiveArrayIterator ( $a[1] ) ); to get sum of each section ...
If I'm understanding you correctly, this function should do the trick:
<?php
function recursive_sum($arr) {
$sum = 0;
foreach($arr as $value) {
if(is_array($value)) {
$sum += recursive_sum($value);
}
else {
$sum += $value;
}
}
return $sum;
}
?>
Just call recursive_sum($a) to get the sum of all the values in your array, like this:
<?php
echo recursive_sum($a);
?>
<? //PHP 5.4+
$a=[];
$a[0][0]=1;
$a[0][1]=3;
$a[0][2]=5;
$a[1][0]=10;
$a[1][1]=2;
$a[1][2]=5;
$a[1][3]=7;
$a[2][0]=9;
$a[2][1]=8;
$a[2][2]=9;
$a[2][3]=8;
$a[3][0]=9;
$a[3][1]=8;
$a[3][2]=9;
$a[3][3]=8;
$a[3][4]=1;
//This is downright evil, but it works.
eval(\array_reduce(
\array_reverse(\array_keys($a)),
static function($eval, $key){
return "foreach(\$a[$key]as\$i$key)$eval+\$i$key";
},
'{echo$sum[]=0') . ';echo"$sum<br/>";}');
?>

Randomize a PHP array with a seed?

I'm looking for a function that I can pass an array and a seed to in PHP and get back a "randomized" array. If I passed the same array and same seed again, I would get the same output.
I've tried this code
//sample array
$test = array(1,2,3,4,5,6);
//show the array
print_r($test);
//seed the random number generator
mt_srand('123');
//generate a random number based on that
echo mt_rand();
echo "\n";
//shuffle the array
shuffle($test);
//show the results
print_r($test);
But it does not seem to work. Any thoughts on the best way to do this?
This question dances around the issue but it's old and nobody has provided an actual answer on how to do it: Can i randomize an array by providing a seed and get the same order? - "Yes" - but how?
Update
The answers so far work with PHP 5.1 and 5.3, but not 5.2. Just so happens the machine I want to run this on is using 5.2.
Can anyone give an example without using mt_rand? It is "broken" in php 5.2 because it will not give the same sequence of random numbers based off the same seed. See the php mt_rand page and the bug tracker to learn about this issue.
Sorry, but accordingly to the
documentation the
shuffle function is seeded automatically.
Normally, you shouldn't try to come up with your own algorithms to randomize things since they are very likely to be biased. The Fisher-Yates algorithm is known to be both efficient and unbiased though:
function fisherYatesShuffle(&$items, $seed)
{
#mt_srand($seed);
for ($i = count($items) - 1; $i > 0; $i--)
{
$j = #mt_rand(0, $i);
$tmp = $items[$i];
$items[$i] = $items[$j];
$items[$j] = $tmp;
}
}
Example (PHP 5.5.9):
php > $original = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
php > $shuffled = (array)$original;
php > fisherYatesShuffle($shuffled, 0);
php > print_r($shuffled);
Array
(
[0] => 6
[1] => 0
[2] => 7
[3] => 2
[4] => 9
[5] => 3
[6] => 1
[7] => 8
[8] => 5
[9] => 4
)
php > $shuffled = (array)$original;
php > fisherYatesShuffle($shuffled, 0);
php > print_r($shuffled);
Array
(
[0] => 6
[1] => 0
[2] => 7
[3] => 2
[4] => 9
[5] => 3
[6] => 1
[7] => 8
[8] => 5
[9] => 4
)
You can use array_multisort to order the array values by a second array of mt_rand values:
$arr = array(1,2,3,4,5,6);
mt_srand('123');
$order = array_map(create_function('$val', 'return mt_rand();'), range(1, count($arr)));
array_multisort($order, $arr);
var_dump($arr);
Here $order is an array of mt_rand values of the same length as $arr. array_multisort sorts the values of $order and orders the elements of $arr according to the order of the values of $order.
The problem you have is that PHP comes with two random number generators built in.
The shuffle() command does not use the mt_rand() random number generator; it uses the older rand() random number generator.
Therefore, if you want shuffle() to use a seeded number sequence, you need to seed the older randomiser, using srand() rather than mt_srand().
In most other cases, you should use mt_rand() rather than rand(), since it is a better random number generator.
The main question involves two parts. One is about how to shuffle. The other is about how to add randomness to it.
A simple solution
This is probably the simplest answer to the main question. It is sufficient for most cases in PHP scripting. But not all (see below).
function /*array*/ seedShuffle(/*one dimentional array*/ $array, /*integer*/ $seed) {
$tmp = array();
for ($rest = $count = count($array);$count>0;$count--) {
$seed %= $count;
$t = array_splice($array,$seed,1);
$tmp[] = $t[0];
$seed = $seed*$seed + $rest;
}
return $tmp;
}
The above method will do, even though it doesn't produce true random shuffles for all possible seed-array combinations. However, if you really want it to be balanced and all, I guess PHP shuldn't be your choice.
A more useful solution for advanced programmers
As stated by André Laszlo, randomization is a tricky business. It is usually best to let a dedicated object handle it. My point is, that you shouldn't need to bother with the randomness when you write the shuffle function. Depending on what degree of ramdomness you would like in your shuffle, you may have a number of PseudoRandom objects to choose from. Thus the above could look like this:
abstract class PseudoRandom {
protected abstract function /*integer*/ nextInt();
public function /*integer*/ randInt(/*integer*/ $limit) {
return $this->nextInt()%$limit;
}
}
function /*array*/ seedShuffle($array, /*PseudoRandom Object*/ $rnd) {
$tmp = array();
$count = count($array);
while($count>0) {
$t = array_splice($array,$rnd->randInt($count--),1);
$tmp[] = $t[0];
}
return $tmp;
}
Now, this solution is the one I would vote for. It separates shuffle codes from randomization codes. Depending on what kind of random you need you can subclass PseudoRandom, add the needed methods and your preferred formulas.
And, as the same shuffle function may be used with many random algorithms, one random algorithm may be used in different places.
In recent PHP versions, seeding the PHP builtin rand() and mt_rand() functions will not give you the same results everytime. The reason for this is not clear to me (why would you want to seed the function anyway if the result is different every time.) Anyway, it seems like the only solution is to write your own random function
class Random {
// random seed
private static $RSeed = 0;
// set seed
public static function seed($s = 0) {
self::$RSeed = abs(intval($s)) % 9999999 + 1;
self::num();
}
// generate random number
public static function num($min = 0, $max = 9999999) {
if (self::$RSeed == 0) self::seed(mt_rand());
self::$RSeed = (self::$RSeed * 125) % 2796203;
return self::$RSeed % ($max - $min + 1) + $min;
}
}
Usage:
// set seed
Random::seed(42);
// echo 10 numbers between 1 and 100
for ($i = 0; $i < 10; $i++) {
echo Random::num(1, 100) . '<br />';
}
The code above will output the folowing sequence every time you run it:
76
86
14
79
73
2
87
43
62
7
Just change the seed to get a completely different "random" sequence
A variant that also works with PHP version 7.2, because the php function create_function is deprecated in the newest php version.
mt_srand($seed);
$getMTRand = function () {
return mt_rand();
};
$order = array_map($getMTRand, range(1, count($array)));
array_multisort($order, $array);
return $array;
I guess this will do the job :
function choose_X_random_items($original_array , $number_of_items_wanted = -1 , $seed = FALSE ){
//save the keys
foreach ($original_array as $key => $value) {
$original_array[$key]['key_memory'] = $key;
}
$original_array = array_values($original_array);
$results = array();
if($seed !== FALSE){srand($seed);}
$main_random = rand();
$random = substr($main_random,0,( $number_of_items_wanted == -1 ? count($original_array) : min($number_of_items_wanted,count($original_array)) ));
$random = str_split($random);
foreach ($random AS $id => $value){
$pick = ($value*$main_random) % count($original_array);
$smaller_array[] = $original_array[$pick];
unset($original_array[$pick]);
$original_array = array_values($original_array);
}
//retrieve the keys
foreach ($smaller_array as $key => $value) {
$smaller_array[$value['key_memory']] = $value;
unset($smaller_array[$value['key_memory']]['key_memory']);
unset($smaller_array[$key]);
}
return $smaller_array;
}
In order to not limit the resulting array, set $number_of_items_wanted to -1
In order to not use a seed, set $seed to FALSE
Seeded shuffle while maintaining the key index:
function seeded_shuffle(array &$items, $seed = false) {
mt_srand($seed ? $seed : time());
$keys = array_keys($items);
$items = array_values($items);
for ($i = count($items) - 1; $i > 0; $i--) {
$j = mt_rand(0, $i);
list($items[$i], $items[$j]) = array($items[$j], $items[$i]);
list($keys[$i], $keys[$j]) = array($keys[$j], $keys[$i]);
}
$items = array_combine($keys, $items);
}
A simple solution:
$pool = [1, 2, 3, 4, 5, 6];
$seed = 'foo';
$randomIndex = crc32($seed) % count($pool);
$randomElement = $pool[$randomIndex];
It might not be quite as random as the Fisher Yates shuffle, but I found it gave me more than enough entropy for what I needed it for.
Based on #Gumbo, #Spudley, #AndreyP answers, it works like that:
$arr = array(1,2,3,4,5,6);
srand(123); //srand(124);
$order = array_map(function($val) {return rand();}, range(1, count($arr)));
array_multisort($order, $arr);
var_dump($arr);
Home Made function, using crc32(note:also returns negative value(https://www.php.net/manual/en/function.crc32.php))
$sortArrFromSeed = function($arr, $seed){
$arrLen = count($arr);
$newArr = [];
$hash = crc32($seed); // returns hash (0-9 numbers)
$hash = strval($hash);
while(strlen($hash) < $arrLen){
$hash .= $hash;
}
for ($i=0; $i<$arrLen; $i++) {
$index = (int) $hash[$i] * (count($arr)/9); // because 0-9 range
$index = (int) $index; // remove decimal
if($index !== 0) $index--;
array_push($newArr, $arr[$index]);
unset($arr[$index]);
$arr = array_values($arr);
}
return $newArr;
};
// TESTING
$arr = ['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'];
$arr = $sortArrFromSeed($arr,'myseed123');
echo '<pre>'.print_r($arr,true).'</pre>';
This seems the easiest for me...
srand(123);
usort($array,function($a,$b){return rand(-1,1);});

Categories