Select and delete random keys from multidimensional array - php

I have one problem with randomness.
I have example array.
$example=array(
'F1' => array('test','test1','test2','test5'),
'F2' => array('test3', 'test4'),
'F3' => array('one','twoo','threee','x'),
'F5' => array('wow')
)
I want to choose random keys from array to other array with specified size. In this second array I want values from other groups.
For example i got
$amounts = array(4,3,1,2,1);
I want to choose randomly specified ammount of variables ($amount) from $example, but of course - always from other groups.
Example result:
$result=(
array(4) => ('test','test4','x','wow'),
array(3) => ('test2','test3','three'),
array(1) => ('test1')
array(2) => ('test5','one')
array(1) => ('twoo')
What I tried so far?
foreach($amounts as $key=>$amount){
$random_key[$key]=array_rand($example,$amount);
foreach($result[$key] as $key2=>$end){
$todelete=array_rand($example[$end]);
$result[$key][$key2]=$example[$amount][$todelete]
}
}
I don't know now, what to fix or to do next.
Thanks for help!

$example = array(
'F1' => array('test', 'test1', 'test2', 'test5'),
'F2' => array('test3', 'test4'),
'F3' => array('one', 'twoo', 'threee', 'x'),
'F5' => array('wow')
);
$amounts = array(4, 3, 1, 2, 1);
$result = array();
$example = array_values($example);
//randomize the array
shuffle($example);
foreach ($example as $group) {
shuffle($group);
}
//sort the example array by child length
usort($example, function ($a, $b) {
return count($b) - count($a);
});
foreach ($amounts as $amount) {
$tmpResult = array();
for ($i = 0; $i < $amount; $i++) {
if(empty($example[$i])){
throw new \InvalidArgumentException('The total number of values in the array exceed the amount inputed');
}
$tmpResult[] = array_pop($example[$i]);
}
$result[] = $tmpResult;
//sort the example array again by child length
usort($example, function ($a, $b) {
return count($b) - count($a);
});
}
print_r($result);
test result:
Array
(
[0] => Array
(
[0] => test5
[1] => x
[2] => test4
[3] => wow
)
[1] => Array
(
[0] => test2
[1] => threee
[2] => test3
)
[2] => Array
(
[0] => test1
)
[3] => Array
(
[0] => twoo
[1] => test
)
[4] => Array
(
[0] => one
)
)

Related

Sort array values based on parent/child relationship

I am trying to sort an array to ensure that the parent of any item always exists before it in the array. For example:
Array
(
[0] => Array
(
[0] => 207306
[1] => Bob
[2] =>
)
[1] => Array
(
[0] => 199730
[1] => Sam
[2] => 199714
)
[2] => Array
(
[0] => 199728
[1] => Simon
[2] => 207306
)
[3] => Array
(
[0] => 199714
[1] => John
[2] => 207306
)
[4] => Array
(
[0] => 199716
[1] => Tom
[2] => 199718
)
[5] => Array
(
[0] => 199718
[1] => Phillip
[2] => 207306
)
[6] => Array
(
[0] => 199720
[1] => James
[2] => 207306
)
)
In the above array this "fails" as [1][2] (Sam) does not yet exist and nor does [4][2] (Tom).
The correct output would be as, in this case, as both Sam and Tom's parents already exist before they appear in the array:
Array
(
[0] => Array
(
[0] => 207306
[1] => Bob
[2] =>
)
[1] => Array
(
[0] => 199714
[1] => John
[2] => 207306
)
[2] => Array
(
[0] => 199730
[1] => Sam
[2] => 199714
)
[3] => Array
(
[0] => 199728
[1] => Simon
[2] => 207306
)
[4] => Array
(
[0] => 199718
[1] => Phillip
[2] => 207306
)
[5] => Array
(
[0] => 199716
[1] => Tom
[2] => 199718
)
[6] => Array
(
[0] => 199720
[1] => James
[2] => 207306
)
)
I found an answer https://stackoverflow.com/a/12961400/1278201 which was very close but it only seems to go one level deep (i.e. there is only ever one parent) whereas in my case there could be 1 or 10 levels deep in the hierarchy.
How do I sort the array so no value can appear unless its parent already exists before it?
This will trivially order the array (in O(n)) putting first all those with no parent, then these whose parent is already in the array, iteratively, until there's no children having the current element as parent.
# map the children by parent
$parents = ['' => []];
foreach ($array as $val) {
$parents[$val[2]][] = $val;
}
# start with those with no parent
$sorted = $parents[''];
# add the children the current nodes are parent of until the array is empty
foreach ($sorted as &$val) {
if (isset($parents[$val[0]])) {
foreach ($parents[$val[0]] as $next) {
$sorted[] = $next;
}
}
}
This code requires PHP 7, it may not work in some cases under PHP 5. - for PHP 5 compatibility you will have to swap the foreach ($sorted as &$val) with for ($val = reset($sorted); $val; $val = next($sorted)):
# a bit slower loop which works in all versions
for ($val = reset($sorted); $val; $val = next($sorted)) {
if (isset($parents[$val[0]])) {
foreach ($parents[$val[0]] as $next) {
$sorted[] = $next;
}
}
}
Live demo: https://3v4l.org/Uk6Gs
I have two different version for you.
a) Using a "walk the tree" approach with recursion and references to minimize memory consumption
$data = [
[207306,'Bob',''], [199730,'Sam',199714],
[199728,'Simon',207306], [199714,'John',207306],
[199716, 'Tom',199718], [199718,'Phillip',207306],
[199720,'James',207306]
];
$list = [];
generateList($data, '', $list);
var_dump($list);
function generateList($data, $id, &$list) {
foreach($data as $d) {
if($d[2] == $id) {
$list[] = $d; // Child found, add it to list
generateList($data, $d[0], $list); // Now search for childs of this child
}
}
}
b) Using phps built in uusort()function (seems only to work up to php 5.x and not with php7+)
$data = [
[207306,'Bob',''], [199730,'Sam',199714],
[199728,'Simon',207306], [199714,'John',207306],
[199716, 'Tom',199718], [199718,'Phillip',207306],
[199720,'James',207306]
];
usort($data, 'cmp');
var_dump($data);
function cmp($a, $b) {
if($a[2] == '' || $a[0] == $b[2]) return -1; //$a is root element or $b is child of $a
if($b[2] == '' || $b[0] == $a[2]) return 1; //$b is root element or $a is child of $b
return 0; // both elements have no direct relation
}
I checked this works in PHP 5.6 and PHP 7
Sample array:
$array = Array(0 => Array(
0 => 207306,
1 => 'Bob',
2 => '',
),
1 => Array
(
0 => 199730,
1 => 'Sam',
2 => 199714,
),
2 => Array
(
0 => 199728,
1 => 'Simon',
2 => 207306,
),
3 => Array
(
0 => 199714,
1 => 'John',
2 => 207306,
),
4 => Array
(
0 => 199716,
1 => 'Tom',
2 => 199718,
),
5 => Array
(
0 => 199718,
1 => 'Phillip',
2 => 207306,
),
6 => Array
(
0 => 199720,
1 => 'James',
2 => 207306,
),
);
echo "<pre>";
$emp = array();
//form the array with parent and child
foreach ($array as $val) {
$manager = ($val[2] == '') ? 0 : $val[2];
$exist = array_search_key($val[2], $emp);
if ($exist)
$emp[$exist[0]][$val[0]] = $val;
else
//print_R(array_search_key(199714,$emp));
$emp[$manager][$val[0]] = $val;
}
$u_emp = $emp[0];
unset($emp[0]);
//associate the correct child/emp after the manager
foreach ($emp as $k => $val) {
$exist = array_search_key($k, $u_emp);
$pos = array_search($k, array_keys($u_emp));
$u_emp = array_slice($u_emp, 0, $pos+1, true) +
$val +
array_slice($u_emp, $pos-1, count($u_emp) - 1, true);
}
print_R($u_emp); //print the final result
// key search function from the array
function array_search_key($needle_key, $array, $parent = array())
{
foreach ($array AS $key => $value) {
$parent = array();
if ($key == $needle_key)
return $parent;
if (is_array($value)) {
array_push($parent, $key);
if (($result = array_search_key($needle_key, $value, $parent)) !== false)
return $parent;
}
}
return false;
}
Find the below code that might be helpful.So, your output is stored in $sortedarray.
$a=array(array(207306,'Bob',''),
array (199730,'Sam',199714),
array(199728,'Simon',207306),
array(199714,'John',207306),
array(199716,'Tom',199718),
array(199718,'Phillip',207306),
array(199720,'James',207306));
$sortedarray=$a;
foreach($a as $key=>$value){
$checkvalue=$value[2];
$checkkey=$key;
foreach($a as $key2=>$value2){
if($key<$key2){
if ($value2[0]===$checkvalue){
$sortedarray[$key]=$value2;
$sortedarray[$key2]=$value;
}else{
}
}
}
}
print_r($sortedarray);
What about this approach:
Create an empty array result.
Loop over your array and only take the items out of it where [2] is empty and insert them into result.
When this Loop is done you use a foreach-Loop inside a while-loop. With the foreach-Loop you take every item out of your array where [2] is already part of result. And you do this as long as your array contains anything.
$result = array();
$result[''] = 'root';
while(!empty($yourArray)){
foreach($yourArray as $i=>$value){
if(isset($result[$value[2]])){
// use the next line only to show old order
$value['oldIndex'] = $i;
$result[$value[0]] = $value;
unset($yourArray[$i]);
}
}
}
unset($result['']);
PS: You may run into trouble by removing parts of an array while walking over it. If you do so ... try to solve this :)
PPS: Think about a break condition if your array have an unsolved loop or a child without an parent.
you can use your array in variable $arr and use this code it will give you required output.
function check($a, $b) {
return ($a[0] == $b[2]) ? -1 : 1;
}
uasort($arr, 'check');
echo '<pre>';
print_r(array_values($arr));
echo '</pre>';

2 arrays match data into 1 array with PHP

I have 2 array's, first array have for example ItemID of my item, second array have description about my item. I want to match data into 1 array.
It looks like:
[rgInventory] => Array
(
[1234567890] => Array
(
[id] => 1234567890
[classid] => 123456789
[instanceid] => 987654321
[amount] => 1
[pos] => 1
)
)
[rgDescriptions] => Array
(
[192837465_918273645] => Array
(
[appid] => 730
[name] => Something
)
)
Items in arrays don't have the same value like ID, but they are in the same order so:
Description for the first item in rgInventory is in the first array inside rgDescriptions.
What should I do to match for example id from rgInventory with name from rgDescriptions in the same array for example $backpack = array();?
Regards for you.
Try this:
<?php
$array1 = array('rgInventory' =>
array(
'1234567890' => array(
'id' => 1234567890,
'classid' => 123456789,
'instanceid' => 987654321,
'amount' => 1,
'pos' => 1
)
)
);
$array2 = array(
'rgDescriptions' => array(
'192837465_918273645' => array(
'appid' => 730, 'name' => 'Something')
)
);
Create new function to combine the two arrays into one array:
function array_sum_recursive($data1, $data2) {
if (!is_array($data1) && !is_array($data2)) {
return $data1 + $data2;
}
// deepest array gets precedence
if (!is_array($data2)) {
return $data1;
}
if (!is_array($data1)) {
return $data2;
}
//merge and remove duplicates
$keys = array_unique(array_merge(array_keys($data1), array_keys($data2)));
foreach ($keys as $key) {
if (isset($data1[$key]) && isset($data2[$key])) {
$result[$key] = array_sum_recursive($data1[$key], $data2[$key]);
} else if (isset($data1[$key])) {
$result[$key] = $data1[$key];
} else {
$result[$key] = $data2[$key];
}
}
if(empty($result)){
echo "no result";
die();
}else{
return $result;
}
}
Put the two array in one array $newarray:
$newonearray = array_sum_recursive($array1, $array2);
echo '<pre>';
print_r($newonearray);
?>
And you will get this:
Array
(
[rgInventory] => Array
(
[1234567890] => Array
(
[id] => 1234567890
[classid] => 123456789
[instanceid] => 987654321
[amount] => 1
[pos] => 1
)
)
[rgDescriptions] => Array
(
[192837465_918273645] => Array
(
[appid] => 730
[name] => Something
)
)
)
Hope this may help.
You can use function each to get each element of both arrays, then merge its with array_merge and save this new item to backup array.
Try something like this
<?php
$rgInventory = ['firstInv' => ['invId' => 1], 'secondInv' => ['invId' => 2]];
$rgDescriptions = ['firstDesc' => ['descId' => 1], 'secondDesc' => ['descId' => 2]];
if (count($rgInventory) && count($rgInventory) == count($rgDescriptions)) {
$backpack = [];
while($inventory = each($rgInventory)) {
$description = each($rgDescriptions);
$item = array_merge($inventory['value'], $description['value']);
$backpack[] = $item;
}
var_dump($backpack);
}
Output will be:
array(2) {
[0]=>
array(2) {
["invId"]=>
int(1)
["descId"]=>
int(1)
}
[1]=>
array(2) {
["invId"]=>
int(2)
["descId"]=>
int(2)
}
}

how to get value from associative array

This is my array :
Array
(
[0] => Array
(
[0] => S No.
[1] => Contact Message
[2] => Name
[3] => Contact Number
[4] => Email ID
)
[1] => Array
(
[0] => 1
[1] => I am interested in your property. Please get in touch with me.
[2] => lopa <br/>(Individual)
[3] => 1234567890
[4] => loperea.ray#Gmail.com
)
[2] => Array
(
[0] => 2
[1] => This user is looking for 3 BHK Multistorey Apartment for Sale in Sohna, Gurgaon and has viewed your contact details.
[2] => shiva <br/>(Individual)
[3] => 2135467890
[4] => sauron82#yahoo.co.in
)
)
How can I retrieve all data element wise?
You can get information about arrays in PHP on the official PHP doc page
You can access arrays using square braces surrounding the key you like to select [key].
So $array[1] will give yoo:
Array
(
[0] => 1
[1] => I am interested in your property. Please get in touch with me.
[2] => lopa <br/>(Individual)
[3] => 1234567890
[4] => loperea.ray#Gmail.com
)
And $array[1][2] will give you:
lopa <br/>(Individual)
Or you can walkt through the elements of an array using loops like the foreach or the for loop.
// perfect for assoc arrays
foreach($array as $key => $element) {
var_dump($key, $element);
}
// alternative for arrays with seamless numeric keys
$elementsCount = count($array);
for($i = 0; $i < $elementsCount; ++$i) {
var_dump($array[$i]);
}
You have integer indexed elements in multidimensional array. To access single element from array, use array name and it's index $myArray[1]. To get inner element of that previous selected array, use second set of [index] - $myArray[1][5] and so on.
To dynamically get all elements from array, use nested foreach loop:
foreach ($myArray as $key => $values) {
foreach ($values as $innerKey => $value) {
echo $value;
// OR
echo $myArray[$key][$innerKey];
}
}
The solution is to use array_reduce:
$header = array_map(
function() { return []; },
array_flip( array_shift( $array ) )
); // headers
array_reduce( $array , function ($carry, $item) {
$i = 0;
foreach( $carry as $k => $v ) {
$carry[$k][] = $item[$i++];
}
return $carry;
}, $header );
First of all we get the header from the very first element of input array. Then we map-reduce the input.
That gives:
$array = [['A', 'B', 'C'], ['a1', 'b1', 'c1'], ['a2', 'b2', 'c2'], ['a3', 'b3', 'c3']];
/*
array(3) {
'A' =>
array(3) {
[0] =>
string(2) "a1"
[1] =>
string(2) "a2"
[2] =>
string(2) "a3"
}
'B' =>
array(3) {
[0] =>
string(2) "b1"
[1] =>
string(2) "b2"
[2] =>
string(2) "b3"
}
'C' =>
array(3) {
[0] =>
string(2) "c1"
[1] =>
string(2) "c2"
[2] =>
string(2) "c3"
}
}
*/
I think this is what you are looking for
$array = Array
(
0=> Array
(
0 => 'S No.',
1 => 'Contact Message',
2 => 'Name',
3 => 'Contact Number',
4 => 'Email ID'
),
1 => Array
(
0 => 1,
1 => 'I am interested in your property. Please get in touch with me.',
2 => 'lopa <br/>(Individual)',
3 => '1234567890',
4 => 'loperea.ray#Gmail.com',
),
2 => Array
(
0 => 2,
1 => 'This user is looking for 3 BHK Multistorey Apartment for Sale in Sohna, Gurgaon and has viewed your contact details.',
2 => 'shiva <br/>(Individual)',
3 => '2135467890',
4 => 'sauron82#yahoo.co.in',
)
);
$result_array = array();
array_shift($array);
reset($array);
foreach($array as $x=>$array2){
foreach($array2 as $i => $arr){
if($i == 1){
$result_array[$x]['Contact Message'] = $arr;
}elseif($i == 2){
$result_array[$x]['Name'] = $arr;
}elseif($i == 3){
$result_array[$x]['Contact Number'] =$arr;
}elseif($i == 4){
$result_array[$x]['Email ID'] = $arr;
}
}
}
print_r($result_array);

How to get the difference of specific data types encoded into arrays

How to get the difference of arrays containing data-types?
First array:
Array
(
[0] => Array
(
[ID] => 21323154
[NAME] => Name_2
[PREVIEW_TEXT] => Text_2
)
)
Second array:
Array
(
[0] => Array
(
[ID] => 543547564
[NAME] => Name_1
[PREVIEW_TEXT] => Text_1
)
[1] => Array
(
[ID] => 222213322
[NAME] => Name_2
[PREVIEW_TEXT] => Text_2
)
[2] => Array
(
[ID] => 333876833
[NAME] => Name_3
[PREVIEW_TEXT] => Text_3
)
)
The result should be an array:
Array
(
[0] => Array
(
[ID] => 543547564
[NAME] => Name_1
[PREVIEW_TEXT] => Text_1
)
[1] => Array
(
[ID] => 333876833
[NAME] => Name_3
[PREVIEW_TEXT] => Text_3
)
)
I tried different options, but they all return the result found the difference, i.e. the first array.
I have a different ID
Just try with:
$output = array_udiff($arraySecond, $arrayFirst, function($a, $b){
return strcmp($a['NAME'], $b['NAME']);;
});
Output:
array(2) {
[0] =>
array(3) {
'ID' =>
int(543547564)
'NAME' =>
string(6) "Name_1"
'PREVIEW_TEXT' =>
string(6) "Text_1"
}
[2] =>
array(3) {
'ID' =>
int(333876833)
'NAME' =>
string(6) "Name_3"
'PREVIEW_TEXT' =>
string(6) "Text_3"
}
}
If both arrays are large, array_diff will be very inefficient, because it compares each element of the first array with each element of the second. A faster solution is to split the process into two steps: first, generate a set of keys to remove:
$remove = array();
foreach($firstArray as $item)
$remove[$item['name']] = 1;
and then iterate over the second array and add "good" items to the result:
$result = array();
foreach($secondArray as $item)
if(!isset($remove[$item['name']]))
$result []= $item;
This will give you linear performance, while array_diff is quadratic.
Diff both arrays against the field (column) you want to, give back only those entries then (thanks to keys):
$col = 'NAME';
$diff = array_intersect_key($b, array_diff(array_column($b, $col), array_column($a, $col)));
print_r($diff);
If you prefer to have this less quadratic but linear, you can also solve it via iteration (inspired by georg's answer but using array_column() again):
$filtered = function ($col, $a, $b) {
return iterator_to_array(call_user_func(function () use ($col, $a, $b) {
$coled = array_flip(array_column($a, $col));
foreach ($b as $bk => $bv) if (!isset($coled[$bv[$col]])) yield $bk => $bv;
}));
};
print_r($filtered('NAME', $a, $b));
With $a and $b as outlined, you will get the following result for both examples:
Array
(
[0] => Array
(
[ID] => 543547564
[NAME] => Name_1
[PREVIEW_TEXT] => Text_1
)
[2] => Array
(
[ID] => 333876833
[NAME] => Name_3
[PREVIEW_TEXT] => Text_3
)
)
// 1.) - diff against column, intersect keys
$filtered = function($col, $a, $b) {
return array_intersect_key($b, array_diff(array_column($b, $col), array_column($a, $col)));
};
// 2.) - iterate and take only unset by column
$filtered = function ($col, $a, $b) {
return iterator_to_array(call_user_func(function () use ($col, $a, $b) {
$coled = array_flip(array_column($a, $col));
foreach ($b as $bk => $bv) if (!isset($coled[$bv[$col]])) yield $bk => $bv;
}));
};
// 3.) - array_udiff against column
$filtered = function($col, $a, $b) {
return array_udiff($b, $a, function ($a, $b) use ($col) {
return strcmp($a[$col], $b[$col]);
});
};
Usage:
print_r($filtered('NAME', $a, $b));
Where $a is the array containing the elements to remove from the $b array. So $a is the first array of the question and $b is the second array of the question.

Intersecting multidimensional array of varying size

I have a multidimensional array that looks like this:
Array
(
[0] => Array
(
[0] => Array
(
[id] => 3
)
[1] => Array
(
[id] => 1
)
[2] => Array
(
[id] => 2
)
[3] => Array
(
[id] => 5
)
[4] => Array
(
[id] => 4
)
)
[1] => Array
(
[0] => Array
(
[id] => 1
)
[1] => Array
(
[id] => 3
)
[2] => Array
(
[id] => 4
)
[3] => Array
(
[id] => 5
)
)
[2] => Array
(
[0] => Array
(
[id] => 3
)
)
)
I need to find a way to return the intersecting value(s). In this case that would be
[id] => 3
The length of the array could be different, so I can't just use array_intersect().
This would be simple if your arrays contained only integers, but as they contain an another array, it gets a bit more complicated. But this should do it:
function custom_intersect($arrays) {
$comp = array_shift($arrays);
$values = array();
// The other arrays are compared to the first array:
// Get all the values from the first array for comparison
foreach($comp as $k => $v) {
// Set amount of matches for value to 1.
$values[$v['id']] = 1;
}
// Loop through the other arrays
foreach($arrays as $array) {
// Loop through every value in array
foreach($array as $k => $v) {
// If the current ID exists in the compare array
if(isset($values[$v['id']])) {
// Increase the amount of matches
$values[$v['id']]++;
}
}
}
$result = array();
// The amount of matches for certain value must be
// equal to the number of arrays passed, that's how
// we know the value is present in all arrays.
$n = count($arrays) + 1;
foreach($values as $k => $v) {
if($v == $n) {
// The value was found in all arrays,
// thus it's in the intersection
$result[] = $v;
}
}
return $result;
}
Usage:
$arrays = array(
array(array('id' => 3), array('id' => 1), array('id' => 2), array('id' => 5), array('id' => 4)),
array(array('id' => 1), array('id' => 3), array('id' => 4), array('id' => 5)),
array(array('id' => 3))
);
print_r(custom_intersect($arrays));
Result:
Array
(
[0] => 3
)
This function isn't perfect: if you have duplicate ID's in one array, it will not work. That would require a bit more code to first make the array values unique, but this will probably work in your case.
You can use array_uintersect() to get the intersection of arrays using a custom comparison function. You have to call it with call_user_func_array() though, as it expects each array as a separate argument:
//build list of parameters for array_uintersect()
$params = array_merge($input, array('compare_func'));
$result = call_user_func_array('array_uintersect', $params);
function compare_func($a, $b) {
return $a['id'] - $b['id'];
}
You can't simply call array_intersect() with call_user_func_array(), because it seems to compare arrays by comparing their string representation (which will always be 'Array').
$a = array(4,3);
$b = array(4,3,2,1);
$c = array(1,2,3,4,5);
$d = array(5,4,3);
$array = array($a,$b,$c,$d);
for ($i=0; $i<count($array); $i++){
if ($i==0){
$array2 = $array[$i];
} else {
$array2 = array_intersect($array2, $array[$i]);
}
}
print_r($array2);`
Result:
3,4
As mentioned in one of the comments of the php.net website (array_intersect function).
$a = array(1,2,3,4,5,2,6,1); /* repeated elements --> $a is not a set */
$b = array(0,2,4,6,8,5,7,9,2,1); /* repeated elements --> $b is not a set */
$ua = array_merge(array_unique($a)); /* now, $a is a set */
$ub = array_merge(array_unique($b)); /* now, $b is a set */
$intersect = array_merge(array_intersect($ua,$ub));
This will return this array:
Array
(
[0] => 1
[1] => 2
[2] => 4
[3] => 5
[4] => 6
)

Categories