array(10) { [0]=> int(5)
[1]=> int(8)
[2]=> int(2)
[3]=> int(0)
[4]=> int(1)
[5]=> int(9)
[6]=> int(1)
[7]=> int(0)
[8]=> int(5)
[9]=> int(4)
}
Ok, so I have an array as above. What I would like to do is get the top x items, change them into 1 and the rest into 0 without messing with the keys so that I can implode it back in the correct order. So if I want top 5, the result should be something like:
array(10) { [0]=> int(1)
[1]=> int(1)
[2]=> int(0)
[3]=> int(0)
[4]=> int(0)
[5]=> int(1)
[6]=> int(0)
[7]=> int(0)
[8]=> int(1)
[9]=> int(1)
}
I tried using sort, but I think it messes up the keys.
Any help would be appreciated. Thanks.
Uhm, I don't know If I understand question, but maybe:
for ($i = 0; $i < $topX; $i++)
$arr[$i] = (int)1;
for ($i = $topX+1;$i < count($arr); $i++)
$arr[$i] = (int)0;
Or add IF:
for ($i = 0; $i < count($arr); $i++)
$arr[$i] = ($arr[$i] < $maxVal) ? (int)1 : (int)0;
Well, I guess this is what you are looking for. I believe that the code is self-explain because I have comment all the important concept in the code.
<?php
function top(&$arr, $top, $start = 0)
{
if (count($arr) > $start) {
// count the number of element that larger
// than the element at start position
$count = 0;
for($i = $start + 1; $i < count($arr); $i++) {
if ($arr[$start] < $arr[$i]) $count++;
}
// if there are more than [$top] number of element
// that is larger than this element
// it cannot be in the [$top] largest number
if ($count >= $top) {
$arr[$start] = 0;
} else {
$arr[$start] = 1;
$top -= 1;
}
// continue to next element
top($arr, $top, $start + 1);
}
}
$arr = array(5, 8, 2, 0, 1, 9, 1, 0, 5, 4);
top($arr, 5);
var_dump($arr);
?>
asort() sort an array and maintain index association.
PHP has some pretty neat array-functions you can work with. Here's one approach:
<?php
function change_top_items($array, $top_x) {
// first, sort the values and preserve the keys
uasort($array, function($a, $b) {
if ($a == $b) return 0;
return ($a < $b) ? -1 : 1;
});
// put the top x values in an array
$compare = array_slice($array, -$top_x);
// now walk through the original array
$modified = array_map(function($value) use ($compare) {
// if the current value is in the top x array
// set it to 1, else set it to 0
return in_array($value, $compare) ? 1 : 0;
}, $array);
// now sort the modified array by keys again and return it
ksort($modified);
return $modified;
}
$a = [5, 8, 2, 0, 1, 9, 1, 0, 5, 4];
print_r(change_top_items($a, 5));
And that's pretty much it.
Output:
Array
(
[0] => 1
[1] => 1
[2] => 0
[3] => 0
[4] => 0
[5] => 1
[6] => 0
[7] => 0
[8] => 1
[9] => 1
)
Here's a working exapmple: http://3v4l.org/HI2EH - As you can also see there, I've used a syntax which is only valid for PHP 5.4+. This concerns the short array notation and the use of closures. But this code can easily be converted into a syntax of the previous PHP versions.
Related
I want the number of occurrences of each element in array.
Note: I can't use inbuilt function like array_count_values()
here's what I have tried.
$a = ['a','b','a','c','a','d'];
$output = [];
for ($i = 0; $i <= count($a);$ i++) {
$count = 1;
for($j = $i + 1; $j < count($a); $j++) {
if ($a[$i] == $a[$j]) {
$count++;
$output[$a[$i]] = $count;
}
}
}
print_r($output);
In PHP7 you can use the NULL coalescing operator to simplify this code:
$a=['a','b','a','c','a','d'];
$output=[];
foreach ($a as $v) {
$output[$v] = ($output[$v] ?? 0) + 1;
}
print_r($output);
Output:
Array
(
[a] => 3
[b] => 1
[c] => 1
[d] => 1
)
Demo on 3v4l.org
As mentioned in the comments array_count_values() is the optimal solution in php. But you must write it out aka show your understanding on how to search an array its rather simple as well.
$a=['a','b','a','c','a','d'];
$output=[];
for($i = 0; $i < count($a); $i++){
if(!isset($output[$a[$i]])){
$output[$a[$i]] = 1;
}
else{
$output[$a[$i]] = $output[$a[$i]] + 1;
}
}
var_dump($output);
//output
array(4) {
["a"] => int(3)
["b"] => int(1)
["c"] => int(1)
["d"] => int(1)
}
<?php
$items=['a','b','a','c','a','d'];
foreach($items as $item) {
$result[$item] ??= 0;
$result[$item] += 1;
}
var_dump($result);
Output:
array(4) {
["a"]=>
int(3)
["b"]=>
int(1)
["c"]=>
int(1)
["d"]=>
int(1)
}
I'm trying to understand how the items from my array are passed to my value compare function when using usort(). A printout of the values of $x and $y for each iteration follows:
Iteration 1:
// $x
array(2) { ["k1"]=> int(21) ["k2"]=> string(1) "e" }
// $y
array(2) { ["k1"]=> int(920) ["k2"]=> string(1) "z" }
Iteration 2:
// $x
array(2) { ["k1"]=> int(842) ["k2"]=> string(1) "t" }
// $y
array(2) { ["k1"]=> int(21) ["k2"]=> string(1) "e" }
Iteration 3:
// $x
array(2) { ["k1"]=> int(920) ["k2"]=> string(1) "z" }
// $y
array(2) { ["k1"]=> int(21) ["k2"]=> string(1) "e" }
Iteration 4:
// $x
array(2) { ["k1"]=> int(842) ["k2"]=> string(1) "t" }
// $y
array(2) { ["k1"]=> int(920) ["k2"]=> string(1) "z" }
My data:
$data = array(
array( 'k1' => 920, 'k2' => 'z' ),
array( 'k1' => 21, 'k2' => 'e' ),
array( 'k1' => 842, 'k2' => 't' )
);
My custom function:
function value_compare_func( $x, $y ) {
if ( $x['k1'] > $y['k1'] ) {
return true;
} elseif ( $x['k1'] < $y['k1'] ) {
return false;
} else {
return 0;
}
}
Sort the array:
usort( $data, 'value_compare_function' );
For the first iteration, $x['k1'] is $data[1]['k1'] and $y['k1'] is $data[0][k1]. Why aren't the items from my $data array passed to value_compare_func() in order? For example, I would have expected $x['k1'] to be $data[0]['k1'] and $y['k1'] to be $data[1]['k1'] for the first iteration, but this isn't the case.
The answer to how these items are passed to the comparison function would require understanding the quicksort algorithm. The gist of it is that some element in the array is assigned as a pivot (it could be any element at all really, but in efficient implementation's it's typically the median element) and then comparison on either side of the pivot is done to sort the array.
This is the underlying implementation of usort in PHP, basically.
So trying to observe the order in which elements are passed to the comparison function is relatively useless. The order is completely unimportant. What's important is that you can rest assured they will always be sorted correctly according to what your callback function returns.
The important thing to note here is that the manual explicitly warns against returning values from the callback that are not integers for a very specific reason (they will be cast to integers) and in your example here you are returning a boolean false from the callback which will become 0 when cast to an integer. Since 0 indicates both values are equal you will not and should not expect a properly sorted array here.
Always make sure to return an integer value of 1 when $a > $b, an integer value of -1, when $a < $b and an integer value of 0 when $a == $b from your callback.
The compare function should return a numerical value. Since you return booleans, the comparison becomes unpredictable as false == 0 and so the distinction between the two is lost in sorting.
You should change your code as follows:
function value_compare_func( $x, $y ) {
if ( $x['k1'] > $y['k1'] ) {
return 1;
} elseif ( $x['k1'] < $y['k1'] ) {
return -1;
} else {
return 0;
}
}
... and the results will be more logical.
I have an array with string indices, that I need partially sorted. That is, some elements must be moved first, but the others should remain untouched in their current (PHP-internal) order:
# The element with key "c" should be first
$foo = array(
"a" => 1,
"b" => 2,
"c" => 3,
"d" => 4,
);
uksort($foo, function ($a, $b) {
if ($a === "c") {
return -1;
} elseif ($b === "c") {
return 1;
}
return 0;
});
var_dump($foo);
What I expected:
array(4) { ["c"]=> int(3) ["a"]=> int(1) ["b"]=> int(2) ["d"]=> int(4) }
//--------------------------^ "a" remains first of the unsorted ones
What I got:
array(4) { ["c"]=> int(3) ["d"]=> int(4) ["b"]=> int(2) ["a"]=> int(1) }
//--------------------------^ "d" moved above "a"
This seems due to the sorting algorithm uksort() uses internally, which destroys the fragile order of elements. Is there any other way to achieve this sorting?
Using any sort function is overkill for this task. You merely need to merge the input array into a lone array containing the element which should come first. The array union operator (+) will do nicely for your associative array (otherwise array_merge() will do).
Codes: (Demos)
if key c is guaranteed to exist:
$foo = array(
"a" => 1,
"b" => 2,
"c" => 3,
"d" => 4,
);
$foo = ['c' => $foo['c']] + $foo;
var_export($foo);
if key c might not exist check for it first:
$bar = array(
"a" => 1,
"b" => 2,
"d" => 4,
);
if (array_key_exists('c', $bar)) {
$bar = ['c' => $bar['c']] + $bar;
}
var_export($bar);
Output:
array (
'c' => 3,
'a' => 1,
'b' => 2,
'd' => 4,
)
and
array (
'a' => 1,
'b' => 2,
'd' => 4,
)
This worked for me and returned:
array(4) { ["c"]=> int(3) ["a"]=> int(1) ["b"]=> int(2) ["d"]=> int(4) }
<?php
# The element with key "c" should be first
$foo = array(
"a" => 1,
"b" => 2,
"c" => 3,
"d" => 4,
);
uksort($foo, function ($a, $b) {
if ($a === "c") {
return -1;
} else
return 1;
});
var_dump($foo);
?>
I have two arrays, $country and $redirect with each entry corresponding with it's exact counterpart e.g. $redirect[1] and $country[1].
After un-setting values throughout the array, I might be left with, for example, an array where only $var[4] and $var[2] are set. I want to re-assign array keys from 0 upwards, so that $var[2] would have it's key re-assigned to $var[0] and $var[4] re-assigned to $var[1].
Essentially the sort() function, but sorting by current array key, as oppose to the numeric/string value of an array.
Is this possible?
Any answers or advice would be greatly appreciated ;)!
UPDATE:
I've attempted to use both ksort() and array_values(), however I'm not sure they're really what I need, as I plan on using the size_of() function.
My code:
$var = array(2 => "value_1", 4 => "value_2", 6 => "value_3");
ksort($var);
for($i = 0, $size = sizeof($var); $i < $size; $i++) {
$var[$i] = "foo";
}
var_dump($var);
Returns:
array(5) { [2]=> string(3) "foo" [4]=> string(7) "value_2" [6]=> string(7) "value_3" [0]=> string(3) "foo" [1]=> string(3) "foo" }
Any additional ideas/answers on how I could get this to work would be greatly appreciated!
Use array_values() (returns "sorted" array):
$var = array(2 => "value_1", 4 => "value_2", 6 => "value_3");
$var = array_values($var);
for($i = 0, $size = sizeof($var); $i < $size; $i++) {
$var[$i] = "foo";
}
var_dump($var);
I have issues sorting an multidimensional array.
The array looks like:
$array = array(
array("token" => array(100, 240, 348, 23, 17),
array("token" => array(293, 28, 283, 2, 28),
array("token" => array(842, 23, 72, 98, 114)
);
Now I want to sort them by "column". That means, the first column of numbers (100, 293, 842) must be sorted, then the second column (but keeping the first column as it is! It may happen that the columns have the same number with multiple rows) and so on.
Actually I tried this to do with usort(), but this will work only when sorting the first column:
function do_sort($a, $b) {
$tok_a = $a["token"];
$tok_b = $b["token"];
if ($tok_a[0] <= $tok_b[0])
return false;
else
return true;
}
usort($array, "do_sort");
How can I do this? Thanks
Here's possible solution:
get rid of 'token', i.e., make 2D array
swap columns and rows (transpose array)
sort each column
swap back columns and rows (get initial structure)
put 'token' back
Code:
function array_transpose(array $array) {
$result = array();
foreach ( $array as $rowNum => $row ) {
foreach ( $row as $colNum => $value ) {
$result[$colNum][$rowNum] = $value;
}
}
return $result;
}
$array = array(
array("token" => array(100, 240, 348, 23, 17)),
array("token" => array(293, 28, 283, 2, 28)),
array("token" => array(842, 23, 72, 98, 114)),
);
// get rid of 'token'
foreach ( $array as &$item ) {
$item = $item['token'];
}
unset($item);
// swap columns and rows
$array = array_transpose($array);
// sort columns
foreach ( $array as &$item ) {
sort($item);
}
unset($item);
// swap back columns and rows
$array = array_transpose($array);
// put 'token' back
foreach ( $array as &$item ) {
$item = array('token' => $item);
}
unset($item);
// display results
foreach ( $array as $row ) {
foreach ( $row['token'] as $value ) {
printf('%-7d', $value);
}
echo "\n";
}
Output:
100 23 72 2 17
293 28 283 23 28
842 240 348 98 114
I think this will do what you are after. I've made some assumptions here (such as $array is really an array, has at least one sub-array, all sub-arrays have token as the key, and all sub-arrays have the same number of elements).
<?php
$array = array(
array("token" => array(100, 240, 348, 23, 17)),
array("token" => array(293, 28, 283, 2, 28)),
array("token" => array(842, 23, 72, 98, 114)),
);
$count_outer = count($array);
$count_inner = count($array[0]['token']);
for ($i=0; $i < $count_inner; $i++) {
$temp_arr = array();
for ($j=0; $j < $count_outer; $j++) {
$temp_arr[] = $array[$j]['token'][$i];
}
sort($temp_arr);
for ($j=0; $j < $count_outer; $j++) {
$array[$j]['token'][$i] = $temp_arr[$j];
}
}
foreach ($array as $value) {
var_dump($value);
echo '<br>';
}
Output:
array(1) { ["token"]=> array(5) {
[0]=> int(100) [1]=> int(23) [2]=>
int(72) [3]=> int(2) [4]=> int(17) } }
array(1) { ["token"]=> array(5) {
[0]=> int(293) [1]=> int(28) [2]=>
int(283) [3]=> int(23) [4]=> int(28) }
} array(1) { ["token"]=> array(5) {
[0]=> int(842) [1]=> int(240) [2]=>
int(348) [3]=> int(98) [4]=> int(114)
} }
Can't you just
foreach ($array as &$item) {
sort($item['token']);
}
Or have I misunderstood the question?