Select multiple random column values from a two-dimensional array - php

I want to select 5 random ID's from my array of rows. Here is my array $test:
$test = [
['id' => 13, 'pets' => 8],
['id' => 15, 'pets' => 8],
['id' => 16, 'pets' => 10],
['id' => 17, 'pets' => 9],
['id' => 18, 'pets' => 10],
['id' => 19, 'pets' => 10],
['id' => 20, 'pets' => 0],
['id' => 21, 'pets' => 8],
['id' => 22, 'pets' => 9],
['id' => 23, 'pets' => 4],
['id' => 24, 'pets' => 0],
['id' => 40, 'pets' => 8],
['id' => 43, 'pets' => 2],
];
How can I select 5 random ID's from the array and put them into a string like this:
$ids = '13,17,18,21,43';
I've tried to use array_rand(), but it does not seem to work for my type of array. I'm not sure if there are any other built in PHP functions that can do this type of job or if I have to create my own function. It would be nice to have my own function like this to plug in the number of required values.

You can use array_column to only get the ID's and shuffle them.
Then use array_slice to get five items and implode.
$id = array_column($arr, "id");
Shuffle($id);
Echo implode(",", array_slice($id, 0, 5));

First extract the id column indexing also by the id, then pick 5 random ones, and finally implode into a comma separated list. Since keys must be unique, this has the added benefit of not returning duplicate ids if there happen to be duplicates in the array:
$ids = implode(',', array_rand(array_column($test, 'id', 'id'), 5));
For a function:
function array_rand_multi($array, $key, $num) {
return implode(',', array_rand(array_column($array, $key, $key), $num));
}

If you want random, unique ids in a random order, I recommend shuffling the array, then isolating upto 5 subarrays, then extracting the id values, then joining with commas. This way array_column() doesn't need to iterate the full array.
Code: (Demo)
shuffle($test);
echo implode(
',',
array_column(
array_slice($test, -5),
'id'
)
);
If you want random, unique ids and don't mind that they will be in the same order as your input rows, then array_rand() can be used.
#AbraCadaver's approach works by applying temporary keys to the input array, picking five random keys, then joining with commas. Because the values inside the rows are never used, null can also be used as array_column()'s second parameter. These approaches should not be used if duplicate ids need to be honored. In other words, because id values are being applied to the first level keys, php will automatically destroy any rows with duplicated ids -- because a single level of an array cannot contain duplicate keys.
One way to avoid potentially destroying data is to call array_rand() on the original indexes of the input array, then filter those unique indexes by 5 randomly selected indexes. (Demo)
echo implode(
',',
array_column(
array_intersect_key(
$test,
array_flip(array_rand($test, 5))
),
'id'
)
);
Finally, if you want 5 randomly selected, randomly ordered ids which may be selected more than once, then just make 5 iterated calls of array_rand(). (Demo)
for ($x = 0, $delim = ''; $x < 5; ++$x, $delim = ',') {
echo $delim . $test[array_rand($test)]['id'];
}
Or (Demo)
echo implode(
',',
array_map(
fn() => $test[array_rand($test)]['id'],
range(1, 5)
)
);

You can proceed like this (short example) :
<?php
$items = array(
array("id" => 43, "pets" =>2),
array("id" => 40, "pets" =>8),
array("id" => 24, "pets" =>0),
array("id" => 23, "pets" =>4),
);
$ids = $items[array_rand($items)]["id"].",".$items[array_rand($items)]["id"].",".$items[array_rand($items)]["id"];
echo $ids;
// Output Example : 24, 40, 23
?>
It will choose a random key from the main array ($items), example : 3, and output the "id" :
$items[3]["id"]
for this example.
Here is a demo : http://sandbox.onlinephpfunctions.com/code/32787091e341cdf8e172d96b065b14b3ca834846

Related

Print unique values from an array column as a comma-separated string

I want to print all unique Department values from a multidimensional array as a comma-separated string, but not all rows have a Department value.
The boiled down version of my array looks like this:
$employee = [
["employee_id" => 1, "Department" => "Tech"],
["employee_id" => 2, "Department" => "Tech"],
["employee_id" => 3],
["employee_id" => 4, "Department" => "Tech"],
["employee_id" => 5],
["employee_id" => 6, "Department" => "Crm"],
["employee_id" => 7],
["employee_id" => 8, "Department" => "Crm"],
["employee_id" => 9, "Department" => "Crm"],
["employee_id" => 10],
["employee_id" => 11, "Department" => "Crm"],
["employee_id" => 12, "Department" => "Crm"]
];
I tried with:
for ($i=0; $i < count($employee); $i++) {
print_r(array_unique($employee[$i]['Department']));
}
But I generate Warnings when I try to access a non-existent Department value.
Expected output:
Tech,Crm
<?php
echo implode(",",array_unique(array_column($employee,'Department')));
Use array_column to filter values of Department column and use array_unique() to have unique values of Department. Now, just implode() them based on ,.
You can avoid calling array_unique() after array_column() by repeating its second parameter as its third parameter. PHP does no allow duplicate kays on the same level as an array. When a repeated Department is encountered, the old value is replaced with its newer, identical value.
Code: (Demo)
echo implode(',', array_column($employee, 'Department', 'Department'));
// Tech,Crm

PHP re-group array by each column to multiple array without loop

PHP re-group array by each column to multiple array without loop
In Laravel DB return, i.e. ->get()->toArray() result is as such:
[
['ts' => 1234, 'a' => 3, 'b' => 2],
['ts' => 1244, 'a' => 2, 'b' => 6],
['ts' => 1254, 'a' => 8, 'b' => 3],
]
Is there any way that I am able to transform the data to as such:
[
['column' => 'a', 'values' => [[1234, 3], [1244, 2], [1254, 8]],
['column' => 'b', 'values' => [[1234, 2], [1244, 6], [1254, 3]],
]
I just wish to know if there's any best / efficient way to render the transformation as described above. Avoid re looping again as data is already available, it can be thought as a data formatting question. Also, I do not wish to use additional array if possible.
Things that I've already looked includes ->get()->map(function()), however this is sequential and I am not able to get values in 2D array as a result.
You'll want to map over your items and return the ts value, as well as the value for each column, respectively:
$items = Model::all();
$result = [
[
'column' => 'a',
'values' => $items->map(function ($item) {
return [$item->ts, $item->a];
})
],
[
'column' => 'b',
'values' => $items->map(function ($item) {
return [$item->ts, $item->b];
})
],
];
If you want to combine the logic for both columns, create a collection of column names, and map over those:
$items = Model::all();
$result = collect(['a', 'b'])->map(function ($column) use ($items) {
$values = $items->map(function ($item) use ($column) {
return [$item->ts, $item->{$column}];
});
return compact('column', 'values');
});

Remove duplicate objects from array based on one value, keep lowest of other value in PHP?

Right now I have an array of objects in PHP, and some of them have a duplicate value for "id". I'm trying to remove the duplicates but I want to keep the one with the lowest value for "qty".
I know how you'd normally remove duplicate whole objects from an array, but not only if one value is a duplicate and how to keep the lower of another value.
Example:
[
{
id: 1,
qty: 200
},
{
id: 1,
qty: 190
},
{
id: 2,
qty: 10
},
{
id: 2,
qty: 12
},
{
id: 2,
qty: 10
},
{
id: 3,
qty: 5
},
{
id: 4,
qty: 5
},
{
id: 4,
qty: 2
},
]
What I want to end up with would be..
[
{
id: 4,
qty: 2
},
{
id: 3,
qty: 5
},
{
id: 2,
qty: 10
},
{
id: 1,
qty: 190
}
]
Is this possible?
That looks almost like JSON, so assuming you $array = json_decode($json, true) to an associative array:
array_multisort(array_column($array, 'qty'), SORT_DESC, $array);
$result = array_column($array, null, 'id');
Extract an array of qty and sort that descending, sorting the original by that
Extract from that an array with id as the key
Extracting with array_column() will cause the last id key to overwrite the previous ones. The last one will be the one with the lowest qty since it was sorted DESCending.
If you need to get it back to a JSON object, then just re-index:
$json = json_encode(array_values($result));
AbraCadaver came up with such a good answer, but I worked hard to come up with mine, so I want to share it in case it is useful for someone. If anything, it may provide useful for an expanded or more complex array. I went the route of creating a nested loop. Here is the code:
$newArray = [];
for ($i = 0; $i < count($myArray); $i++)
{
$id_column = array_column($newArray, 'id');
$qty_column = array_column($newArray, 'qty');
if (!in_array($myArray[$i]['id'],$id_column)) {
array_push($newArray, $myArray[$i]);
}
else {
$id_pos = array_search($myArray[$i]['id'],$id_column);
if ($myArray[$i]['qty'] < $qty_column[$id_pos])
{
array_splice($newArray,$id_pos,1,$myArray[$i]);
}
}
}
Basically I create a new empty array. I loop through each element of the original array to see if it's in the new array. If not, I add it, and if it is already in the new array, then I check the new Array to see if the qty for that id is higher, if so, I splice in the current row.
It is not necessary to involve a sorting algorithm -- that would only negatively impact the script performance.
If a row's id is not yet presented in the output array, push it into the output array with a temporary first level key. If the row's id is already in the output array, but has a lesser qty, then overwrite the row in the output array.
This task never needs more than one loop. If you want to remove the temporary first level keys, then call array_values() after looping.
Code: (Demo)
$data = [
['id' => 1, 'qty' => 200],
['id' => 1, 'qty' => 190],
['id' => 2, 'qty' => 10],
['id' => 2, 'qty' => 12],
['id' => 2, 'qty' => 10],
['id' => 3, 'qty' => 5],
['id' => 4, 'qty' => 5],
['id' => 4, 'qty' => 2],
];
$result = [];
foreach ($data as $row) {
if (!isset($result[$row['id']]) || $row['qty'] < $result[$row['id']]['qty']) {
$result[$row['id']] = $row;
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'id' => 1,
'qty' => 190,
),
1 =>
array (
'id' => 2,
'qty' => 10,
),
2 =>
array (
'id' => 3,
'qty' => 5,
),
3 =>
array (
'id' => 4,
'qty' => 2,
),
)
Here is one of the possible answers, assuming $your_input contains your data as a string…
Method 1
<?php
$your_input=preg_replace('#\s*\{\s*id\s*:\s*([^,]+?),\s*qty\s*:\s*([^,\}]+?)\s*\}\s*(,)?#','{"id":"$1","qty":"$2"}$3',$your_input);
$your_input=json_decode($your_input,true);
$result=[];
foreach($your_input as $a)
if (!array_key_exists($a['id'],$result) or $a['qty']<$result[$a['id']]['qty'])
{
$result[$a['id']]['id']=$a['id'];
$result[$a['id']]['qty']=$a['qty'];
}
$result=json_encode(array_values($result),JSON_PRETTY_PRINT);
echo '<pre>';
print_r($result);
Method 2
<?php
$your_input=str_replace([' ',':'],['"','":'],$your_input);
$your_input=json_decode($your_input,true);
usort($your_input,function($a,$b){return $a['qty']==$b['qty']?0:($a['qty']>$b['qty']?-1:1);});
foreach($your_input as $b) $result[$b['id']]=['id'=>$b['id'],'qty'=>$b['qty']];
$result=json_encode(array_values($result),JSON_PRETTY_PRINT);
echo '<pre>';
print_r($result);
After Method 1 or Method 2 the $result variable should contains the following data string…
[
{
"id": 1,
"qty": 190
},
{
"id": 2,
"qty": 10
}
]

Adding an array of values to an existing array

I've got a foreach where I create an array out of id's, based on the submitted selected checkboxes from my form (which are `checkbox[$id]. So I end up with:
Where 1, 2 and 3 are the submitted id's from the form. So far so good.
Now I also have an input field amount[$id]in my form. When selecting a checkbox, I can enter an amount for that row and submit the results. I need to add the values of amount to my array if id's. My end result should look like this:
[1 => ['amount' => '10'], 2 => ['amount' => '12'], 3 => ['amount' => '5'] // And so on
I tried merging, and array_push, but I seem to be doing it wrong, since I cannot figure it out. Any pointers?
Something like this should work:
$result = [];
$ids = [1,2,3]; // I suppose it is `$_POST['ids']`
$amounts = [1 => 10, 2 => 11, 3 => 22]; // I suppose it is `$_POST['amount']`
foreach ($ids as $id) {
if (!empty($amounts[$id])) {
$result[$id] = ['amount' => $amounts[$id]];
}
}
Using array_combine as advised in comments can be used only if sizes of arrays are equal. So if you have something like:
$ids = [1,2,4];
$amounts = [1 => 10, 2 => 11, 3 => 0, 4 => 22];
print_r(array_combine($ids, $amounts)); // PHP Warning
And second fact - array_combine won't create values as arrays. So
$ids = [1,2,3];
$amounts = [1 => 10, 2 => 11, 3 => 10];
print_r(array_combine($ids, $amounts)); // no subarrays here

Get max value from each column of a 2-dimensional array

I have an array within an array, for example:
[
[0, 20, 5],
[5, 0, 15],
[5, 10, 0]
]
I need to get the max number in each column.
The max of [0 , 5 , 5] is 5, so that goes into the result array.
The max of [20, 0, 10] is 20, so that goes into the result array.
The max of [5, 15, 0] is 15, so that goes into the result array.
The final result array must contain [5, 20, 15].
First, the array has to be transposed (flip the rows and columns):
function array_transpose($arr) {
$map_args = array_merge(array(NULL), $arr);
return call_user_func_array('array_map', $map_args);
}
(taken from Is there better way to transpose a PHP 2D array? - read that question for an explanation of how it works)
Then, you can map the max function on the new array:
$maxes = array_map('max', array_transpose($arr));
Example: http://codepad.org/3gPExrhO
Output [I think this is what you meant instead of (5, 15, 20) because you said index 1 should be max of (20, 0, 10)]:
Array
(
[0] => 5
[1] => 20
[2] => 15
)
Without the splat operator, array_map() with max() will return the max value for each row. ([20, 15, 10])
With the splat operator to transpose the data structure, the max value for each column is returned.
Code: (Demo)
$array = [
[0, 20, 5],
[5, 0, 15],
[5, 10, 0]
];
var_export(
array_map('max', ...$array)
);
Output:
array (
0 => 5,
1 => 20,
2 => 15,
)

Categories