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);
?>
Related
I need to merge multiple arrays into one where a specific key & its value are same. Here is the Sample_Array1
array(n) {
[0]=> array {
["a"]=> "m1"
["b"]=> "x2"
}
[1]=> array {
["a"]=> "n1"
["b"]=> "y2"
} ....
Sample_Array2 with one common key & other different ones.
array(n) {
[0]=> array {
["b"]=> "x2"
["c"]=> "p1"
}
[1]=> array {
["b"]=> "x2"
["d"]=> "q1"
}
[2]=> array {
["b"]=> "y2"
["e"]=> "r1"
} ....
Need to merge / append Sample_Array2 to Sample_Array1 where key-"b" & its value are same. The expected output:
array(n) {
[0]=>
array(2) {
["a"]=> "m1"
["b"]=> "x2"
["c"]=> "p1"
["d"]=> "q1"
}
[1]=>
array(2) {
["a"]=> "n1"
["b"]=> "y2"
["e"]=> "r1"
} ....
I have tried so many similar questions but couldn't find the exact result.
PHP merge arrays with a condition The answer given on this link is not solving the purpose, its making different array for each new key, while I need to append the new keys in one array.
This should work, assuming you have the "b" index in all sub arrays.
$array1 = array();
$array1[] = array("a" => "m1", "b" => "x2", "c" => null);
$array1[] = array("a" => "n1", "b" => "y2");
$array2 = array();
$array2[] = array("b" => "x2", "c" => "p1");
$array2[] = array("a" => null, "b" => "x2", "d" => "q1");
$array2[] = array("b" => "y2", "e" => "r1");
function merge_on_key($array1, $array2, $key) {
$result_array = array();
foreach($array1 as $key1 => $sub_array1) {
$merged_array = array();
$sub_array1 = array_filter($sub_array1);
foreach($array2 as $key2 => $sub_array2) {
$sub_array2 = array_filter($sub_array2);
if($sub_array1[$key] == $sub_array2[$key]) {
$merged_array = array_merge($sub_array1, $sub_array2, $merged_array);
unset($array2[$key2]);
}
}
if (!empty($merged_array)) {
$result_array[] = $merged_array;
}
}
return array_merge($result_array, $array2);
}
$final_array = merge_on_key($array1, $array2, "b");
print_r($final_array);
In case you have to match the "b" index within the $array1 itself too, then simply use it twice:
$array1 = merge_on_key($array1, $array1, "b");
$final_array = merge_on_key($array1, $array2, "b");
i really have no idea what you want to achieve here - but based on your description the following code works
$arrA = [
0 =>
[
'a' => 'm1',
'b' => 'x2'
],
1 =>
[
'a' => 'n1',
'b' => 'y2'
]
];
$arrB = [
0 =>
[
'b' => 'x2',
'c' => 'p1',
],
1 =>
[
'b' => 'x2',
'd' => 'q1',
],
2 =>
[
'b' => 'y2',
'e' => 'r1',
],
];
foreach($arrB AS $arrData)
{
foreach($arrData AS $key => $val)
{
if ((isset($arrData['a']) && $arrData['a'] == $arrA[0]['a']) || (isset($arrData['b']) && $arrData['b'] == $arrA[0]['b']))
{
$arrA[0][$key] = $val;
}
elseif ((isset($arrData['b']) && $arrData['b'] == $arrA[1]['a']) || (isset($arrData['b']) && $arrData['b'] == $arrA[1]['b']))
{
$arrA[1][$key] = $val;
}
}
}
print_r($arrA);
Created the arrays similar to yours. $new_array is the resultant array that you are looking for.
$a=array();
$a[0]=array('a'=>'m1', 'b'=>'x2');
$a[1]=array('a'=>'n1', 'b'=>'y2');
$b=array();
$b[0]=array('b'=>'x2', 'c'=>'p1');
$b[1]=array('b'=>'x2', 'd'=>'q1');
$b[2]=array('b'=>'y2', 'e'=>'r1');
foreach($a as $row){
//echo '<pre>'; print_r($row);
foreach($b as $c=>$row1){
//echo '<pre>'; print_r($row1);echo $c;die;
if($row['b']==$row1['b']){
$new_array[]=array_merge($row, $row1);
}
}
}echo '<pre>'; print_r($new_array);
// Gather all values of b from both arrays
$all_b = array_unique(array_merge(array_column($arr1, 'b'), array_column($arr2, 'b')));
$res = [];
// For each b value
foreach($all_b as $b) {
$temp = [];
// Scan the arrays for items with the same b value
foreach($arr1 as $a1) {
if ($a1['b'] == $b) $temp = array_merge($temp, $a1);
}
foreach($arr2 as $a2) {
if ($a2['b'] == $b) $temp = array_merge($temp, $a2);
}
// Save them to new array
$res[] = $temp;
}
print_r($res);
demo on eval
I want to make some encryption and write numbers
I used:
$a = [100,101,102,103,104,105]
function decrition (array $a){
return preg_replace('/101/','a',$a);
}
And it's returns me all letters "a" for each 101 in array.
How can I change next? 101 to "b", 102 to "c" etc.
return preg_replace('[101|102|103|104|105]','a',$a);
this method replace all this numbers to letter "a"
return preg_replace('[101|102|103|104|105','a|b|c|d|e',$a);
unfortunately it's not working
Why do you try to treat it as a string?
<?php
$a = [ 101, 102, 103 ];
$replace_array = array(101 => "a", 102 => "b");
$b = array_map(function($val) use ($replace_array) {
return (isset($replace_array[$val]) ? $replace_array[$val] : $val);
}, $a);
var_dump($a, $b);
Gives the following output:
array(3) {
[0]=>
int(101)
[1]=>
int(102)
[2]=>
int(103)
}
array(3) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
int(103)
}
Maybe you are looking for something like this?
$test = str_replace($a, array('a','b','c','d','e','f'), $a);
print_r($test);
This solution works
return str_replace(['101', '102', '103', '104', '105'], ['a', 'b', 'c', 'd', 'e'], $a);
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 some problems during working with arrays using array_merge function. Here an example:
First example:
$first = array('01' => 1, '03' => 73);
$second = array('14'=>11, '15' => 23);
var_dump(array_merge($first, $second));
Result is:
array(4) { ["01"]=> int(1) ["03"]=> int(73) [0]=> int(11) [1]=> int(23) }
Expected:
array(4) { ["01"]=> int(1) ["03"]=> int(73) [14]=> int(11) [15]=> int(23) }
Second example:
$first = array('01'=>3, '03'=>10);
$second = array('05'=>44, '07'=>3);
var_dump(array_merge($first,$second));
Result is(as expected):
array(4) { ["01"]=> int(3) ["03"]=> int(10) ["05"]=> int(44) ["07"]=> int(3) }
Third example:
var_dump(array_merge(array("somekey"=> array("some value"))));
Result is(as expected):
array(1) { ["somekey"]=> array(1) { [0]=> string(10) "some value" } }
Fourth example:
var_dump(array_merge(array("34"=> array("some value"))));
Result is:
array(1) { [0]=> array(1) { [0]=> string(10) "some value" } }
Expected:
array(1) { [0]=> array(1) { ["34"]=> string(10) "some value" } }
var_dump(array_merge(array("34"=> array("some value"))));
As you can see from third and fourth examples I set string for keys but the result was not as expected.
What is wrong or incorrect of understanding?
Thanks for helping.
Edited. Why (example first and second) the result is different, but the keys are string and consist of only with digest?
Quoting from the manual:
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.
Keys with a leading zero are being treated as strings, while numeric keys without a leading zero are being treated as numeric
If you want to retain the keys exactly as they are:
$first = array('01' => 1, '03' => 73);
$second = array('14'=>11, '15' => 23);
var_dump(
array_combine(
array_merge(
array_keys($first), array_keys($second)
),
array_merge(
$first, $second
)
)
);
as long as keys are unique between $first and $second
Definition : Merges the elements of one or more arrays together so that the values of one are appended to the end of the previous one. It returns the resulting array.
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.
INPUT
<?php
$array1 = array("color" => "red", 2, 4);
$array2 = array("a", "b", "color" => "green", "shape" => "trapezoid", 4);
$result = array_merge($array1, $array2);
print_r($result);
?>
OUTPUT
Array
(
[color] => green
[0] => 2
[1] => 4
[2] => a
[3] => b
[shape] => trapezoid
[4] => 4
)
Hope it helps:)
Try array_merge() like below:-
$first = array('01' => 1, '03' => 73);
$second = array('14'=>11, '15' => 23);
$output = array_merge($first, $second);
print_r($output);
EDIT:-
$first = array('01' => 1, '03' => 73);
$second = array('14'=>11, '15' => 23);
$output = $first + $second;
print_r($output);
Is there a native PHP function that can remove a set of keys from a array?
for eg. if I have a array like
array('a' => 'aaa', 'b' => 'bbb', 'c' => 'ccc', 'd' => 'ddd');
and I want to remove 'b', 'c' and get array('a' => 'aaa', 'd' => 'ddd'); ?
It's array_diff_key.
$input = array(...);
$remove = array_flip(array('a', 'b')); // 'a' and 'b' are the keys to remove
$output = array_diff_key($input, $remove);
See it in action.
$array = array('a', 'b', 'c', 'd')
foreach($array as $k=>$v){
if (in_array($v,array('b','c'))) unset($array[$k]);
}
An alternate to everyone else's answer, though all valid in their own way, is the array_splice function.
$foo = Array(
'a' => 'aaaa',
'b' => 'bbbb',
'c' => 'cccc',
'd' => 'dddd'
);
var_dump($foo);
array_splice($foo, 1, 2);
var_dump($foo);
Which produces:
array(4) {
["a"]=>
string(4) "aaaa"
["b"]=>
string(4) "bbbb"
["c"]=>
string(4) "cccc"
["d"]=>
string(4) "dddd"
}
array(2) {
["a"]=>
string(4) "aaaa"
["d"]=>
string(4) "dddd"
}
If you don't have too many fields to remove, you can use unset():
unset($foo['b']);
unset($foo['c']);
var_dump($foo)