Sorting multidimensional number array in PHP - php

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?

Related

How to swap largest and smallest number in multidimensional array?

I'm writing a program that swaps largest and smallest number in multidimensional array. Largest number should be on the place where is the smallest, and smallest where is the largest, and the idea is to use pure logic, without any php function that could help me out.
Please check my code and help me about this problem.
For instance:
$array = [
[45, 456, 321, 344, 567],
[100, 434, 173, 400, 789],
[191, 211, 457, 809, 900],
[431, 323, 432, 805, 906],
[708, 232, 897, 101, 696]
];
New order should be:
$array = [
[906, 456, 321, 344, 567],
[100, 434, 173, 400, 789],
[191, 211, 457, 809, 900],
[431, 323, 432, 805, 45],
[708, 232, 897, 101, 696]
]
I've tried with adding and changing code with this piece of code, but it doesn't give me right results...
$min_index = $max_index = 0;
foreach($array as $k => $v){
if($v < $array[$min_index]){
$min_index = $k;
}
if($v > $array[$max_index]){
$max_index = $k;
}
}
$min = $array[$min_index];
$array[$min_index] = $array[$max_index];
$array[$max_index] = $min;
$array = [
[45, 456, 321, 344, 567],
[100, 434, 173, 400, 789],
[191, 211, 457, 809, 900],
[431, 323, 432, 805, 906],
[708, 232, 897, 101, 696]
];
$intRows = 4;
$intCols = 4;
$intMaxRow = $intMinRow = $intMaxCol = $intMinCol = 0;
$minIndex = $maxIndex = 1;
for($row = 0; $row < $intRows; $row++)
{
for($col = 0; $col < $intCols; $col++)
{
if($array[$row][$col] > $maxIndex)
{
$maxIndex = $array[$row][$col];
$intMaxRow = $row;
$intMaxCol = $col;
}
if($array[$row][$col] < $minIndex)
{
$minIndex = $array[$row][$col];
$intMinRow = $row;
$intMinCol = $col;
}
}
}
$arrNxm[$intMinRow][$intMinCol] = $minIndex;
$arrNxm[$intMaxRow][$intMaxCol] = $maxIndex;
echo "<pre>";
var_dump($arrNxm);
echo "</pre>";
You've got some logical issues in your code which is causing it to not work. You are going along the right lines though.
Issue 1
You have set the number of rows and columns incorrectly. There are not 4, there are 5 of each:
$intRows = 5;
$intCols = 5;
Issue 2
The next issue is with:
$minIndex = $maxIndex = 1;
If $minIndex is 1, then nothing in your array can be lower than it, so it's never going to be updated from 1. This should be something like:
$minIndex = 100000; // This is an arbitrary choice to fix the issue
$maxIndex = 1;
Issue 3
Next, you use $arrNxm in your code. There is no $arrNxm, it should be $array.
Issue 4
Finally, this is wrong because you are putting the minimum value back into the minimum position in the array:
$arrNxm[$intMinRow][$intMinCol] = $minIndex;
$arrNxm[$intMaxRow][$intMaxCol] = $maxIndex;
You just need to swap $minIndex and $maxIndex around:
$array[$intMinRow][$intMinCol] = $maxIndex;
$array[$intMaxRow][$intMaxCol] = $minIndex;
Full code
The fully working code is:
$array = [
[45, 456, 321, 344, 567],
[100, 434, 173, 400, 789],
[191, 211, 457, 809, 900],
[431, 323, 432, 805, 906],
[708, 232, 897, 101, 696]
];
$intRows = 5;
$intCols = 5;
$intMaxRow = $intMinRow = $intMaxCol = $intMinCol = 0;
$minIndex = 100000;
$maxIndex = 1;
for($row = 0; $row < $intRows; $row++)
{
for($col = 0; $col < $intCols; $col++)
{
if($array[$row][$col] > $maxIndex)
{
$maxIndex = $array[$row][$col];
$intMaxRow = $row;
$intMaxCol = $col;
}
if($array[$row][$col] < $minIndex)
{
$minIndex = $array[$row][$col];
$intMinRow = $row;
$intMinCol = $col;
}
}
}
$array[$intMinRow][$intMinCol] = $maxIndex;
$array[$intMaxRow][$intMaxCol] = $minIndex;
echo "<pre>";
var_dump($array);
echo "</pre>";
This outputs:
array(5) {
[0]=>
array(5) {
[0]=>
int(906)
[1]=>
int(456)
[2]=>
int(321)
[3]=>
int(344)
[4]=>
int(567)
}
[1]=>
array(5) {
[0]=>
int(100)
[1]=>
int(434)
[2]=>
int(173)
[3]=>
int(400)
[4]=>
int(789)
}
[2]=>
array(5) {
[0]=>
int(191)
[1]=>
int(211)
[2]=>
int(457)
[3]=>
int(809)
[4]=>
int(900)
}
[3]=>
array(5) {
[0]=>
int(431)
[1]=>
int(323)
[2]=>
int(432)
[3]=>
int(805)
[4]=>
int(45)
}
[4]=>
array(5) {
[0]=>
int(708)
[1]=>
int(232)
[2]=>
int(897)
[3]=>
int(101)
[4]=>
int(696)
}
}
Whereby 45 and 906 are swapped in position as you wanted.

Codeigniter - merge multiple associative array with condition

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

Combining keys by similar values in PHP

I am trying to combine keys when they have similar values, and expect the result as a string. Actually I had no idea how to achieve this, although I run a few tests.
$array = array(
'a' => 65,
'b' => 31,
'c' => 100,
'd' => 31,
'e' => 31,
'f' => 31,
'h' => 23,
'i' => 23,
'j' => 23,
'k' => 23,
'l' => 48,
'm' => 48,
);
$results = array();
foreach ($array as $k => $v) {
// This is my attempt among others to no luck.
// Can not use array_key_exists because values are unpredictable.
$similars[$v] = $k;
$results[$v] = implode(", ", array_unique($similars)) . ' : ' . $v;
}
var_dump(implode("\n ", $results));
You can view the output:
http://codepad.org/ECekF3dq
I am almost there, but obviously wrong :(
Not expected:
string(72) "a : 65
a, f, c : 31
a, b, c : 100
a, f, c, k : 23
a, f, c, k, m : 48"
Expected:
a : 65
b, d, e, f : 31
c : 100
h, i, j, k : 23
l, m : 48
Those with the same values should collapse as one line.
Thanks for any hint.
You want to make $similars into an array of arrays. That way you can keep all the keys with that value.
$results = array();
$similars = array();
foreach ($array as $k => $v) {
if(!isset($similars[$v])){
$similars[$v] = array($k);
}
else{
$similars[$v][] = $k;
}
$results[$v] = implode(", ", $similars[$v]) . ' : ' . $v;
}
var_dump(implode("\n ", $results));
There might be a more elegant way of doing this, but I'd probably use two simple foreach loops:
function parseArray($data)
{
$tmp = array();
foreach ($data as $key => $item) {
// Check if we already created this key
if (isset($tmp[$item])) {
// Append if so
$tmp[$item] .= ', '.$key;
} else {
// Init if not
$tmp[$item] = $key;
}
}
// Now we stringify the tmp array.
$result = '';
foreach ($tmp as $key => $value) {
$result .= "$key : $value\n";
}
return $result;
}
$tmp = array();
foreach ($array as $k => $v) $tmp[$v][] = $k;
foreach ($tmp as $k => $v) echo implode(', ', $v) . ' : ' . $k . "\n<br />";
I think reversing the keys and values could be beneficial. Turn the integers into keys, and the chars into array values. This would enable you to maintain the desired one-to-many relationship:
$array = array(
'a' => 65,
'b' => 31,
'c' => 100,
'd' => 31,
'e' => 31,
'f' => 31,
'h' => 23,
'i' => 23,
'j' => 23,
'k' => 23,
'l' => 48,
'm' => 48,
);
$results = array();
foreach ($array as $k => $v) {
$results[$v][]=$k;
}
foreach ($results as $k=>$v) {
echo implode(',',$v) . " : " . $k . "\r\n";
}
the multidimensional array $results equals:
array(5) {
[65]=>
array(1) {
[0]=>
string(1) "a"
}
[31]=>
array(4) {
[0]=>
string(1) "b"
[1]=>
string(1) "d"
[2]=>
string(1) "e"
[3]=>
string(1) "f"
}
[100]=>
array(1) {
[0]=>
string(1) "c"
}
[23]=>
array(4) {
[0]=>
string(1) "h"
[1]=>
string(1) "i"
[2]=>
string(1) "j"
[3]=>
string(1) "k"
}
[48]=>
array(2) {
[0]=>
string(1) "l"
[1]=>
string(1) "m"
}
}
Final output:
a : 65
b,d,e,f : 31
c : 100
h,i,j,k : 23
l,m : 48
Try this
$array = array('a' => 65,'b' => 31,'c' => 100,'d' => 31,'e' => 31,'f' => 31,'h' => 23,
'i' => 23,'j' => 23,'k' => 23,'l' => 48,'m' => 48);
$similars = array();
foreach ($array as $k => $v) {
// This is my attempt among others to no luck.
// Can not use array_key_exists because values are unpredictable.
$key = array_search($v, $similars);
if (isset($similars[$key])) {
unset($similars[$key]);
$similars[$key.','.$k] = $v;
}
else
$similars[$k] = $v;
}
print_r($similars);
//display as expected
foreach($similars as $k => $v)
echo "</br>".$k.':'.$v;

PHP: Check for duplicate values in a multidimensional array

I have this issue with multidimensional arrays.
Given the following multidimensional array:
Array(
[0] => Array("a", "b", "c")
[1] => Array("x", "y", "z")
[2] => Array("a", "b", "c")
[3] => Array("a", "b", "c")
[4] => Array("a", "x", "z")
)
I want to check its values and find duplicates (i.e. keys 0, 2 and 3) leaving just one key - value pair deleting the others, resulting in somthing like this:
Array(
[0] => Array("a", "b", "c")
[1] => Array("x", "y", "z")
[2] => Array("a", "x", "z")
)
How can I do that??
This will remove duplicate items from your array using array_unique():
$new_arr = array_unique($arr, SORT_REGULAR);
You can simply do it using in_array()
$data = Array(
0 => Array("a", "b", "c"),
1 => Array("x", "y", "z"),
2 => Array("a", "b", "c"),
3 => Array("a", "b", "c"),
4 => Array("a", "x", "z"),
);
$final = array();
foreach ($data as $array) {
if(!in_array($array, $final)){
$final[] = $array;
}
}
which will get you something like
array(3) {
[0] => array(3) {
[0] => string(1) "a"
[1] => string(1) "b"
[2] => string(1) "c"
}
[1] => array(3) {
[0] => string(1) "x"
[1] => string(1) "y"
[2] => string(1) "z"
}
[2] => array(3) {
[0] => string(1) "a"
[1] => string(1) "x"
[2] => string(1) "z"
}
}
You can go smart with serialization for comparison of arrays.
var_dump(makeUnique($data));
function makeUnique(array $data)
{
$serialized = array_map(create_function('$a', 'return serialize($a);'), $data);
$unique = array_unique($serialized);
return array_intersect_key($unique, $data);
}
Have fun
$arr = ...;
$final = array();
sort($arr);
foreach ($arr as $el) {
if (!isset($prev) || $el !== $prev)
$final[] = $el
$prev = $el;
}
This is a more efficient1 solution (log n + n instead of quadratic) but it relies on a total order between all the elements of the array, which you may not have (e.g. if the inner arrays have objects).
1 More efficient than using in_array. Turns out array_unique actually uses this algorithm, so it has the same shortcomings.
To check using array_unique on multidimensional arrays, you need to flatten it out like so, using implode.
$c=count($array)
for($i=0;$i<$c;$i++)
{
$flattened=implode("~",$array[$i]);
$newarray[$i]=$flattened;
}
if(count(array_unique($newarray)
<count($newarray))
{
//returns true if $array contains duplicates
//can also use array_unique on $newarray
//to remove duplicates, then explode,
//to return to default state
}
Hope this is helpful, took sometime to get it.
**This is example array**
$multi_com=[
[
{
"combination_id": "19"
},
{
"combination_id": "20"
},
{
"combination_id": "21"
}
],
[
{
"combination_id": "18"
},
{
"combination_id": "20"
},
{
"combination_id": "22"
}
],
[
{
"combination_id": "20"
},
{
"combination_id": "21"
}
]
]
**This is sample code**
$array1 = [];
$array2 = [];
$status = false;
foreach ($multi_com as $key => $val) {
foreach ($val as $key2 => $val2) {
if (count($array1) !== 0) {
$array_res = in_array($val2->combination_id, $array1);
if ($array_res) {
$array2[] = $val2->combination_id;
}
}
}
if (!$status) {
for ($x = 0; $x < count($val); $x++) {
$array1[] = $val[$x]->combination_id;
}
} else {
$array1 = [];
$array1 = $array2;
$array2 = [];
}
$status = true;
}
return $array1;

Merging overlapping ranges in PHP arrays?

I have an array in the following format:
array(
0 => array(1, 5),
1 => array(4, 8),
2 => array(19, 24),
3 => array(6, 9),
4 => array(11, 17),
);
Where each item is a X-to-Y range. What I would like to merge the overlapping ranges in the array, to get something more like this:
array(
0 => array(1, 9), // 1-5, 4-8 and 6-9 are overlapping, so they are merged
1 => array(11, 17),
2 => array(19, 24),
);
What would be the best way to accomplish this?
Untested, but the idea here is to sort the data first by the first element, then merge subsequent elements with the previous one as long as possible.
usort($data, function($a, $b)
{
return $a[0] - $b[0];
});
$n = 0; $len = count($data);
for ($i = 1; $i < $len; ++$i)
{
if ($data[$i][0] > $data[$n][1] + 1)
$n = $i;
else
{
if ($data[$n][1] < $data[$i][1])
$data[$n][1] = $data[$i][1];
unset($data[$i]);
}
}
$data = array_values($data);
$input = array( 0 => array(1, 5),
1 => array(4, 8),
2 => array(19, 24),
3 => array(6, 9),
4 => array(11, 17),
);
$tmpArray = array();
foreach($input as $rangeSet) {
$tmpArray = array_unique(array_merge($tmpArray,range($rangeSet[0],$rangeSet[1])));
}
sort($tmpArray);
$oldElement = array_shift($tmpArray);
$newArray = array(array($oldElement));
$ni = 0;
foreach($tmpArray as $newElement) {
if ($newElement > $oldElement+1) {
$newArray[$ni++][] = $oldElement;
$newArray[$ni][] = $newElement;
}
$oldElement = $newElement;
}
$newArray[$ni++][] = $oldElement;
var_dump($newArray);
Alright, drafted this up, so it may have quirks. Tested it with the data seen below and seemed to work just fine. May not be the best way to do it, but it is one way and it does work. Questions let me know.
function combineRange($array) {
if (is_array($array)) {
// Sort the array for numerical order
sort($array);
// Set Defaults
$prev = array();
$prev_key = null;
foreach ($array as $key => $item) {
// First time around setup default data
if (empty($prev)) {
$prev = $item;
$prev_key = $key;
continue;
}
if ($item[0] >= $prev[0] && $item[0] <= $prev[1]) {
// Incase the last number was less than do not update
if ($array[$prev_key][1] < $item[1])
$array[$prev_key][1] = $item[1];
unset($array[$key]);
}else {
$prev_key = $key;
}
$prev = $item;
}
}
return $array;
}
$array = array(
5 => array(13, 16),
0 => array(1, 5),
1 => array(4, 8),
2 => array(19, 24),
3 => array(6, 9),
4 => array(11, 17),
6 => array(21, 30),
);
var_dump(combineRange($array));
Outputs:
array(3) {
[0]=>
array(2) {
[0]=>
int(1)
[1]=>
int(9)
}
[3]=>
array(2) {
[0]=>
int(11)
[1]=>
int(17)
}
[5]=>
array(2) {
[0]=>
int(19)
[1]=>
int(30)
}
}
Hope it works for ya!
EDIT
I see I was beaten out by an hour =\ Oh well! I am still posting as it is a different method, granted I would probably choose konforce's method instead.

Categories