Related
I'm making a program to take fingerprint values using a winnowing algorithm. which is where I have to take the smallest value from an array. but not only the smallest, there are conditions where if you find the same value, the rightmost value will be taken along with the index value. example array (3,5,5,8,6,7). from this example, I want to take the index of the second number 5, which is index 2, not index 1.
but when I try to get the same value as the rightmost position use the function min () in php, but the one I get is always the leftmost position.
I expect the output of
$a = array(3,5,5,8,6,7,6);
be
[3,0], [5,2], [8,3], [6,6], [7,5]
but the actual output is
[3,0], [5,1], [8,3], [6,4], [7,5]
Just a quick alternative, if you use array_flip() which swaps the value and key over, then this will automatically overwrite any previous key value, so when you flip [1=>5, 2=>5] then the second one will overwrite the first.
So
$a = array(3,5,5,8,6,7,6);
print_r(array_flip($a));
gives...
Array
(
[3] => 0
[5] => 2
[8] => 3
[6] => 6
[7] => 5
)
Use the value from an array as an index to filter out the most right value
$values = array(3,5,5,8,6,7,6);
$result = [];
foreach ($values as $index => $value) {
$result[$value] = [$value, $index];
}
print_r(array_values($result));
Use array_unique to solve this problem
$a = array(3,5,5,8,6,7,6);
$unique = array_unique($a);
print_r($unique);
answer should be
Array
(
[0] => 3
[1] => 5
[3] => 8
[4] => 6
[5] => 7
)
This question already has answers here:
Difference between array_map, array_walk and array_filter
(5 answers)
Closed 2 years ago.
I looked into the similar topics in web as well stack overflow, but could get this one into my head clearly. Difference between array_map, array_walk and array_filter
<?php
error_reporting(-1);
$arr = array(2.4, 2.6, 3.5);
print_r(array_map(function($a) {
$a > 2.5;
},$arr));
print_r(array_filter($arr, function($a){
return $a > 2.5;
}));
?>
The above code returns me a filtered array whose value is > 2.5. Can i achieve what an array_filter does with an array_map?.
All three, array_filter, array_map, and array_walk, use a callback function to loop through an array much in the same way foreach loops loop through an $array using $key => $value pairs.
For the duration of this post, I will be referring to the original array, passed to the above mentioned functions, as $array, the index, of the current item in the loop, as $key, and the value, of the current item in the loop, as $value.
array_filter is likened to MySQL's SELECT query which SELECTs records but doesn't modify them.
array_filter's callback is passed the $value of the current loop item and whatever the callback returns is treated as a boolean.
If true, the item is included in the results.
If false, the item is excluded from the results.
Thus you might do:
<pre><?php
$users=array('user1'=>array('logged_in'=>'Y'),'user2'=>array('logged_in'=>'N'),'user3'=>array('logged_in'=>'Y'),'user4'=>array('logged_in'=>'Y'),'user5'=>array('logged_in'=>'N'));
function signedIn($value)
{
if($value['logged_in']=='Y')return true;
return false;
}
$signedInUsers=array_filter($users,'signedIn');
print_r($signedInUsers);//Array ( [user1] => Array ( [logged_in] => Y ) [user3] => Array ( [logged_in] => Y ) [user4] => Array ( [logged_in] => Y ) )
?></pre>
array_map on the other hand accepts multiple arrays as arguments.
If one array is specified, the $value of the current item in the loop is sent to the callback.
If two or more arrays are used, all the arrays need to first be passed through array_values as mentioned in the documentation:
If the array argument contains string keys then the returned array
will contain string keys if and only if exactly one array is passed.
If more than one argument is passed then the returned array always has
integer keys
The first array is looped through and its value is passed to the callback as its first parameter, and if a second array is specified it will also be looped through and its value will be sent as the 2nd parameter to the callback and so-on and so-forth for each additional parameter.
If the length of the arrays don't match, the largest array is used, as mentioned in the documentation:
Usually when using two or more arrays, they should be of equal length
because the callback function is applied in parallel to the
corresponding elements. If the arrays are of unequal length, shorter
ones will be extended with empty elements to match the length of the
longest.
Each time the callback is called, the return value is collected.
The keys are preserved only when working with one array, and array_map returns the resulting array.
If working with two or more arrays, the keys are lost and instead a new array populated with the callback results is returned.
array_map only sends the callback the $value of the current item not its $key.
If you need the key as well, you can pass array_keys($array) as an additional argument then the callback will receive both the $key and $value.
However, when using multiple arrays, the original keys will be lost in much the same manner as array_values discards the keys.
If you need the keys to be preserved, you can use array_keys to grab the keys from the original array and array_values to grab the values from the result of array_map, or just use the result of array_map directly since it is already returning the values, then combine the two using array_combine.
Thus you might do:
<pre><?php
$array=array('apple'=>'a','orange'=>'o');
function fn($key,$value)
{
return $value.' is for '.$key;
}
$result=array_map('fn',array_keys($array),$array);
print_r($result);//Array ( [0] => a is for apple [1] => o is for orange )
print_r(array_combine(array_keys($array),$result));//Array ( [apple] => a is for apple [orange] => o is for orange )
?></pre>
array_walk is very similar to foreach($array as $key=>$value) in that the callback is sent both a key and a value. It also accepts an optional argument if you want to pass in a 3rd argument directly to the callback.
array_walk returns a boolean value indicating whether the loop completed successfully.
(I have yet to find a practical use for it)
Note that array_walk doesn't make use of the callback's return.
Since array_walk returns a boolean value, in order for array_walk to affect something,
you'll need to reference &$value so you have that which to modify or use a global array.
Alternatively, if you don't want to pollute the global scope, array_walk's optional 3rd argument can be used to pass in a reference to a variable with which to write to.
Thus you might do:
<pre><?php
$readArray=array(1=>'January',2=>'February',3=>'March',4=>'April',5=>'May',6=>'June',7=>'July',8=>'August',9=>'September',10=>'October',11=>'November',12=>'December');
$writeArray=array();
function fn($value,$key,&$writeArray)
{
$writeArray[$key]=substr($value,0,3);
}
array_walk($readArray,'fn',&$writeArray);
print_r($writeArray);//Array ( [1] => Jan [2] => Feb [3] => Mar [4] => Apr [5] => May [6] => Jun [7] => Jul [8] => Aug [9] => Sep [10] => Oct [11] => Nov [12] => Dec )
?></pre>
array_filter returns the elements of the original array for which the function returns true.
array_map returns an array of the results of calling the function on all the elements of the original array.
I can't think of a situation where you could use one instead of the other.
array_map Returns an array containing all the elements of array after applying the callback function to each one.
for example:
$a=array("a","bb","ccd","fdjkfgf");
$b = array_map("strlen",$a);
print_r($b);
//output
Array
(
[0] => 1 //like strlen(a)
[1] => 2 //like strlen(bb)
[2] => 3 //like strlen(ccd)
[3] => 7 //like strlen(fdjkfgf)
)
whereas array_filter return only those elements of array for which the function is true
example: remove "bb" value from array
function test_filter($b)
{
if($b=="bb")
{
return false;
}
else
{
return true;
}
}
$a=array("a","bb","ccd","fdjkfgf");
$b = array_filter($a,"test_filter");
print_r($b);
//output
Array
(
[0] => a //test_filter() return true
[2] => ccd //test_filter() return true
[3] => fdjkfgf //test_filter() return true
)
array_filter works without a callable (function) being passed, whereas for array_map it is mandatory.
e.g.
$v = [true, false, true, true, false];
$x = array_filter($v);
var_dump($x);
array(3) { [0]=> bool(true) [2]=> bool(true) [3]=> bool(true) }
array_walk changes the actual array passed in, whereas array_filter and array_map return new arrays, this is because the array is passed by reference.
array_map has no collateral effects while array_map never changes its arguments.
The resulting array of array_map/array_walk has the same number of
elements as the argument(s); array_filter picks only a subset of the
elements of the array according to a filtering function. It does
preserve the keys.
Example:
<pre>
<?php
$origarray1 = array(2.4, 2.6, 3.5);
$origarray2 = array(2.4, 2.6, 3.5);
print_r(array_map('floor', $origarray1)); // $origarray1 stays the same
// changes $origarray2
array_walk($origarray2, function (&$v, $k) { $v = floor($v); });
print_r($origarray2);
// this is a more proper use of array_walk
array_walk($origarray1, function ($v, $k) { echo "$k => $v", "\n"; });
// array_map accepts several arrays
print_r(
array_map(function ($a, $b) { return $a * $b; }, $origarray1, $origarray2)
);
// select only elements that are > 2.5
print_r(
array_filter($origarray1, function ($a) { return $a > 2.5; })
);
?>
</pre>
Result:
Array
(
[0] => 2
[1] => 2
[2] => 3
)
Array
(
[0] => 2
[1] => 2
[2] => 3
)
0 => 2.4
1 => 2.6
2 => 3.5
Array
(
[0] => 4.8
[1] => 5.2
[2] => 10.5
)
Array
(
[1] => 2.6
[2] => 3.5
)
I want to compare every element of array with one another.
$char=array();
for($i=0;$i<=10;$i++)
{
$char[$i]=rand(0,35);
}
I want to compare every element of $char array. If there is any value repeated than it should change value and select another random value which should be unique in array..
In this particular example, where the range of possible values is very small, it would be better to do this another way:
$allPossible = range(0, 35);
shuffle($allPossible);
// Guaranteed exactly 10 unique numbers in the range [0, 35]
$char = array_slice($allPossible, 0, 10);
Or with the equivalent version using array_rand:
$allPossible = range(0, 35);
$char = array_rand(array_flip($allPossible), 10);
If the range of values were larger then this approach would be very wasteful and you should go with checking for uniqueness on each iteration:
$char = array();
for ($i = 0; $i < 10; ++$i) {
$value = null;
// Try random numbers until you find one that does not already exist
while($value === null || in_array($value, $char)) {
$value = rand(0, 35);
}
$char[] = $value;
}
However, this is a probabilistic approach and may take a lot of time to finish depending on what the output of rand happens to be (it's going to be especially bad if the number of values you want is close to the number of all possible values).
Additionally, if the number of values you want to pick is largish (let's say more than 50 or so) then in_array can prove to be a bottleneck. In that case it should be faster to use array keys to check for uniqueness instead of values, since searching for the existence of a key is constant time instead of linear:
$char = array();
for ($i = 0; $i < 100; ++$i) {
$value = null;
// Try random numbers until you find one that does not already exist
while($value === null || array_key_exists($char, $value)) {
$value = rand(0, 1000);
}
$char[$value] = $value;
}
$char = array_values($char); // reindex keys to start from 0
To change any repeated value for a random one, you should loop through the array twice:
$cont= 0;
foreach($char as $c){
foreach($char as $d){
if($c == $d){
//updating the value
$char[$cont] = rand(0,35);
}
}
$cont++;
}
But what I don't know is if the random value can also be repeated. In that case it would not be so simple.
I've taken this code from the PHP Manual page for rand()
<?php
function uniqueRand($n, $min = 0, $max = null)
{
if($max === null)
$max = getrandmax();
$array = range($min, $max);
$return = array();
$keys = array_rand($array, $n);
foreach($keys as $key)
$return[] = $array[$key];
return $return;
}
?>
This function generates an array which has a size of $n and you can set the min and max values like in rand.
So you could make use of it like
uniqueRand(10, 0, 35);
Use array_count_values() first on the $char array.
Afterwards you can just loop all entries with more than 1 and randomize them. You have to keep checking until all counts are 1 tho. As even the random might remake a duplicate again.
I sugggest two option to make random array:
<?php
$questions = array(1, 2, 3, 4, 5, ..., 34, 35);
$questions = shuffle($questions);
?>
after that you choose the top 10 elements.
you can try this code to replace any repeated value.
for ($i = 0; $i < count($char); $i++) {
for ($n = 0; $n < count($char); $n++) {
if($char[$i] == $char[$n]){
$char[$i] = rand(0,35);
}
}
}
The function array_unique() obtains all unique values from an array, keyed by their first occurrence.
The function array_diff() allows to remove values from one array that are inside another array.
Depending on how you need to have (or not have) the result keyed or the order of keys preserved you need to do multiple steps. Generally it works as I outline in the following paragraphs (with PHP code-examples):
In an array you've got N elements of which Nu are unique.
$N = array(...);
$Nu = array_unique($N);
The number of random elements r you need then to replace the duplicates are the count of N minus the count of Nu. As the count of N is generally a useful value, I also assign it to nc:
$nc = count($N);
$r = $nc - count($Nu);
That makes r an integer ranging from 0 to count(N) - 1:
0 : no duplicate values / all values are unique
1 : one duplicate value / all but one value are unique
...
count(N) - 1 : all duplicate values / no unique value
So in case you you need zero random values ($r === 0) the input $N is the result. This boundary condition is the second most simple result (the first simple result is an input array with no members).
For all other cases you need r random unique values. In your question you write from 0 to 35. However this can not be the full story. Imagine your input array has got 36 duplicated values, each number in the range from 0 to 35 is duplicated once. Adding random numbers from the range 0 to 35 again to the array would create duplicates again - guaranteed.
Instead I've read your question that you are just looking for unique values that are not yet part of the input array.
So you not only you need r random values (Nr), but they also must not be part of N or Nu so far.
To achieve that you only need to create count(N) unique values, remove the unique values Nu from these to ensure nothing duplicates values in Nu. As this the theoretical maximum and not the exact number needed, that array is used to obtain the slice of exactly r elements from:
$Nr = array_slice(array_diff(range(0, $nc - 1), $Nu), 0, $r);
If you also want to have these new values to be added shuffled as range(0, $nc - 1) is ordered, you can do the following:
shuffle($Nr);
That should bring the randomness you seem to ask for in your question back into the answer.
That now leaves you with the unique parts of the original array $Nu and r new values in $Nr. Merging both these arrays will give you a result array which ignores key => value relations (the array is re-index):
array_merge($Nu, $Nr);
For example with an exemplary array(3, 4, 2, 1, 4, 0, 5, 0, 3, 5) for $N, the result this gives is:
Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
[4] => 0
[5] => 5
[6] => 7
[7] => 9
[8] => 6
[9] => 8
)
As you can see all the unique values (0-5) are at the beginning followed by the new values (6-9). Original keys are not preserved, e.g. the key of value 5 was 6 originally, now it is 5.
The relation or key => value are not retained because of array_merge(), it does re-index number keys. Also next to unique numbers in Nr keys also need to be unique in an array. So for every new number that is added to the unqique existing numbers, a key needs to be used that was a key of a duplicate number. To obtain all keys of duplicate numbers the set of keys in the original array is reduced by the set of keys of all for matches of the duplicate numbers (keys in the "unique array" $Nu):
$Kr = array_keys(array_diff_assoc($N, $Nu));
The existing result $Nr can now be keyed with with these keys. A function in PHP to set all keys for an array is to use the function array_combine():
$Nr = array_combine($Kr, $Nr);
This allows to obtain the result with key => value relations preserved by using the array union operator (+):
$Nu + $Nr;
For example with the $N from the last example, the result this gives is:
Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
[5] => 0
[6] => 5
[4] => 8
[7] => 6
[8] => 9
[9] => 7
)
As you can now see for the value 5 it's key 6 has been preserved as well as for the value 0 which had the key 5 in the original array and now as well in the output instead of the key 4 as in the previous example.
However as now the keys have been preserved for the first occurrences of the original values, the order is still changed: First all previously unique values and then all new values. However you might want to add the new values in place. To do that, you need to obtain the order of the original keys for the new values. That can be done by mapping the order by key and the using array_multisort() to sort based on that order.
Because this requires passing return values via parameters, this requires additional, temporary variables which I've chosen to introduce starting with the letter V:
// the original array defines the order of keys:
$orderMap = array_flip(array_keys($N));
// define the sort order for the result with keys preserved
$Vt = $Nu + $Nr;
$order = array();
foreach ($Vt as $key => $value) {
$order[] = $orderMap[$key];
}
Then the sorting is done (here with preserving keys):
// sort an array by the defined order, preserve keys
$Vk = array_keys($Vt);
array_multisort($order, $Vt, $Vk);
The result then is:
array_combine($Vk, $Vt);
Again with the example values from above:
Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
[4] => 7
[5] => 0
[6] => 5
[7] => 8
[8] => 6
[9] => 9
)
This example output shows nicely that the keys are ordered from 0 to 9 as they were are well in the input array. Compared with the previous output you can for example see that the first added value 7 (keyed 4) is at the 5th position - same as the value keyed 4 in the original array. The order of the keys have been obtained as well.
If that is the result you strive for you can short-cut the path to this step as well by iterating the original arrays keys and in case each of those keys is not the first value of any duplicate value, you can pop from the new values array instead:
$result = array();
foreach ($N as $key => $value) {
$result[$key] = array_key_exists($key, $Nu) ? $Nu[$key] : array_pop($Nr);
}
Again with the example array values the result (varies from previous because $Nr is shuffled:
Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
[4] => 7
[5] => 0
[6] => 5
[7] => 8
[8] => 9
[9] => 6
)
Which in the end might be the simplest way to answer your question. Hope this helps you answering the question. Keep the following in mind:
divide your problem:
you want to know if a value is unqiue or not - array_unique() helps you here.
you want to create X new unique numbers/values. array_diff() helps you here.
align the flow:
obtain unique numbers first.
obtain new numbers first.
use both to process the original array.
Like in this example:
// original array
$array = array(3, 4, 2, 1, 4, 0, 5, 0, 3, 5);
// obtain unique values (1.)
$unique = array_unique($array);
// obtain new unique values (2.)
$new = range(0, count($array) - 1);
$new = array_diff($new, $unique);
shuffle($new);
// process original array (3.)
foreach ($array as $key => &$value) {
if (array_key_exists($key, $unique)) {
continue;
}
$value = array_pop($new);
}
unset($value, $new);
// result in $array:
print_r($array);
Which then (exemplary because of shuffle($new)) outputs:
Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
[4] => 9
[5] => 0
[6] => 5
[7] => 8
[8] => 7
[9] => 6
)
I have an array:
$a = array('color' => 'green', 'format' => 'text', 'link_url');
and another:
$b = array('zero', 'one', 'two', 'three', 'test' => 'ok', 'four');
And with array_merge() I have an array like this:
Array
(
[color] => green
[format] => text
[0] => link_url
[1] => zero
[2] => one
[3] => two
[4] => three
[test] => ok
[5] => four
)
Why PHP sets array key as above? Why not like this:
Array
(
[color] => green
[format] => text
[2] => link_url
[3] => zero
[4] => one
[5] => two
[6] => three
[test] => ok
[8] => four
)
That's because numeric IDs are counted separately from seeing indices. The string indices have no number and are not counted.
Quoting from the PHP manual for your original array definitions:
The key is optional. If it is not specified, PHP will use the increment of the largest previously used integer key.
and from the docs on array_merge():
If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, however, the arrays contain numeric keys, the later value will not overwrite the original value, but will be appended.
Values in the input array with numeric keys will be renumbered with incrementing keys starting from zero in the result array.
So it's all quite explicitly documented
Well, if you look at the original array, should be clear:
array(3) {
["color"]=>
string(5) "green"
["format"]=>
string(4) "text"
[0]=>
string(8) "link_url"
}
You appear to have assumed an ordering or a congruity with non-numeric keys, which does not exist.
The numeric keys have an order and this is represented in their new values; the string keys are not part of that ordering system and thus do not affect those new numeric values.
This is simply the way it is and it makes complete sense.
Please check the doc :
If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, however, the arrays contain numeric keys, the later value will not overwrite the original value, but will be appended.
Values in the input array with numeric keys will be renumbered with incrementing keys starting from zero in the result array.
Ref: http://www.php.net/manual/en/function.array-merge.php
$array = array (0.1 => 'a', 0.2 => 'b');
echo count ($array);
It overwrites first array element by second, just because, I used float with 0.
And hence output of above code is 1, instead of 2.
Why PHP round array index down to 0 ?
The array keys are interpreted as numeric, but numeric keys must be integers, Therefore, both float values are cast (truncated) to integer zero and 0.2 overwrites 0.1.
var_dump($array);
array(1) {
[0]=>
string(1) "b"
}
Make the array keys strings if you want to use non integer values:
$array = array ("0.1" => 'a', "0.2" => 'b');
echo count($array);
// 2
array(2) {
["0.1"]=>
string(1) "a"
["0.2"]=>
string(1) "b"
}
Only integer is allowed as key of the array.
See what we get if I print_r($array):
Array ( [0] => b )
However you can do like this:
$array = array ('0.1' => 'a', '0.2' => 'b');
Now print_r says this:
Array ( [0.1] => a [0.2] => b )
Array indices cannot be floats. They must be either integers or strings. If you would try to var_dump($array); you would see that your array looks something like this:
array(1) {
[0]=> string(1) "b"
}
You are effectively trying to set value for key 0 twice.
You cannot use floats as numeric keys. 0.1 and 0.2 both get converted to 0
Either you have to use integers or strings. Therefore, your options are:
$array = array ('0.1' => 'a', '0.2' => 'b');
Or:
$array = array (1 => 'a', 2 => 'b');
Let's see what the PHP's own excellent manual says about arrays (emphasis mine):
The key can either be an integer or a string. The value can be of any
type.
Additionally the following key casts will occur: [...] Floats are also cast to
integers, which means that the fractional part will be truncated.
So, if you look at your array:
<?php
$array = array (0.1 => 'a', 0.2 => 'b');
var_dump($array); // let's see what actually *is* in the array
echo count ($array);
you'll get this back:
array(1) {
[0]=>
string(1) "b"
}
1
So, first your array is { 0 => 'a' }, then becomes { 0 => 'b' }. The computer did exactly what you asked it to, even if not what you intended.
Possible solution: pass the array keys as strings - there is no conversion to int, and it works as expected.
$array = array ('0.1' => 'a', '0.2' => 'b');
You must use quote on non-integer keys
$array = array ('0.1' => 'a', '0.2' => 'b');
echo count($array);
You are storing 'a' into the 0.1th element and 'b' into the 0.2nd element. This is impossible. Array indexes must be integers.
Perhaps you are wanting to use associative arrays?
$array = array ('0.1' => 'a', '0.2' => 'b');
As per the php.net document on arrays for having keys:
Additionally the following key casts will occur:
Floats are also cast to integers, which means that the fractional part
will be truncated. E.g. the key 8.7 will actually be stored under 8.
i tried dumping and interpreting the result... 0.1 and 0.2 will be interpreted as 0 and the latter overwrites the former, end result is that the array key remains 0 and value is set as b.
Hence there's nothing wrong with this behavior.
its because floats are casted to integers, so the second entry overwrites the first.
Actually you are doing this:
$array = array (0 => 'a', 0 => 'b');
echo count ($array);
Php.net Array:
"Floats are also cast to integers, which means that the fractional part will be truncated. E.g. the key 8.7 will actually be stored under 8."