Convert array variables to associative array [duplicate] - php

This question already has answers here:
for vs foreach vs while which is faster for iterating through arrays in php
(6 answers)
Closed 3 years ago.
In PHP, what is quickest way to turn the following array variables:
$id = [11,12,13];
$code = ['1234','5678','9012'];
$qty = [3,4,5];
$amount = [12.34,23.45,34.56];
Into an array of associative arrays, like the following:
[
['id'=>11,'code'=>'1234','qty'=>3,'amount'=>12.34],
['id'=>12,'code'=>'5678','qty'=>4,'amount'=>23.45],
['id'=>13,'code'=>'9012','qty'=>5,'amount'=>34.56],
]
Currently, I'm doing the following to convert the data.
$max = count($id);
$data = [];
for ($i=0; $i<$max; $i++) {
$data[] = [
'id' => $id[$i],
'code' => $code[$i],
'qty' => $qty[$i],
'amount' => $amount[$i]
];
}
My application does this a lot, and looking if there are ways to decrease processing time.
Currently using PHP version 5.6

foreach is typically the fastest method of the "general" approaches used to accomplish your desired end-results. This is due to the count() call prior to issuing for() accompanied with an incremental variable to determine the size and placement of the array to iterate over.
Benchmarks: https://3v4l.org/ejIl5
Benchmark 1-5000 https://3v4l.org/IOlAm
$data = [];
foreach($id as $i => $v) {
$data[] = [
'id' => $v,
'code' => $code[$i],
'qty' => $qty[$i],
'amount' => $amount[$i]
];
}
//Execution time: ~0.00000200 seconds
$max = count($id);
$data = [];
for ($i=0; $i<$max; $i++) {
$data[] = [
'id' => $id[$i],
'code' => $code[$i],
'qty' => $qty[$i],
'amount' => $amount[$i]
];
}
//Execution time ~0.00000600 seconds
array_map was the slowest
$data = array_map(function($a, $b, $c, $d) {
return [
'id' => $a,
'code' => $b,
'qty' => $c,
'amount' => $d
];
}, $id, $code, $qty, $amount);
//Execution time: ~0.00001000 seconds
The benchmark used executes a dry-run for each of the approaches to
reduce OP code optimization issues that would typically be implemented
in a production environment.
As an added bonus from the "general" approaches, I also ran a benchmark of an optimized version of the double-ended iteration approach (for ($i=0; $i<ceil($max/2); $i++)). https://3v4l.org/KHUul and 1-5000 https://3v4l.org/Mg95n which had wildly different values with the smaller array sizes, ranging from 0.00030208 seconds to 0.00000095 seconds, but on average was slower than the general for() loop.
As with any benchmarks, the results may vary for your particular environment, settings and are only meant to serve as a generalization of what could be. Please be sure to benchmark your preferred methods in your specific environment to determine which is best.

you can use foreach as well. I am not sure it will more efficient way but it can help you.
$id = [11,12,13];
$code = ['1234','5678','9012'];
$qty = [3,4,5];
$amount = [12.34,23.45,34.56];
foreach ($id as $key=>$val)
{
$array[$key]["id"]= $val;
$array[$key]["code"]= $code[$key];
$array[$key]["qty"]= $qty[$key];
$array[$key]["amount"]= $amount[$key];
}

My below code will optimize the loop by reducing the loop execution to half and will gives your expected result.
$id = [11,12,13,56,34,23,34];
$code = ['1234','5678','9012','4343','4543','4642','534'];
$qty = [3,4,5,6,7,8,3];
$amount = [12.34,23.45,34.56,66.34,75.32,54.3,23.2];
$max = count($id);
$loopRun = 0;
$data = [];
$halfCount = ceil($max/2);
for ($i=0; $i < $halfCount; $i++) {
$loopRun++;
$data[$i] = [
'id' => $id[$i],
'code' => $code[$i],
'qty' => $qty[$i],
'amount' => $amount[$i]
];
$data[$max-($i+1)] = [
'id' => $id[$max-($i+1)],
'code' => $code[$max-($i+1)],
'qty' => $qty[$max-($i+1)],
'amount' => $amount[$max-($i+1)]
];
}
echo "Loop Run Count : ".$loopRun."<br>";
ksort($data);
Demo Link

Related

Basic array build

Guess this is a basic question.
How can I make an array similar to this, using a foreach loop?
[
[
'ProductGuid' => '27760c24',
'BaseAmountValue' => 240,
'Quantity' => 1,
'Discount' => 0,
'AccountNumber' => 1000,
'Unit' => 'parts',
],
[
'ProductGuid' => '27760c24',
'BaseAmountValue' => 250,
'Quantity' => 1,
'Discount' => 0,
'AccountNumber' => 1000,
'Unit' => 'parts',
]
],
The following is rejected by the API, i'm trying to connect to:
$arr = array();
foreach($items as $item) {
$arr[]['ProductGuid'] = $item->guid;
$arr[]['BaseAmountValue'] = $item->price;
$arr[]['Quantity'] = $item->qty;
$arr[]['Discount'] = $item->discount;
$arr[]['AccountNumber'] = 1000;
$arr[]['Unit'] = 'parts';
}
Hope one of you will be able to help me :)
An alternative to the other two correct answers but without manually setting the array index or use any temporary variables:
$arr = [];
foreach($items as $item) {
$arr[] = [
'ProductGuid' => $item->guid,
'BaseAmountValue' => $item->price,
'Quantity' => $item->qty,
'Discount' => $item->discount,
'AccountNumber' => 1000,
'Unit' => 'parts',
];
}
Martin explained the actual issue so well so no need to go through it again.
Using the given code, you create new inner rows in your array in each line of that loop. The following code will solve that:
$arr = array();
foreach($items as $item) {
$mappedItem = [];
$mappedItem['ProductGuid'] = $item->guid;
$mappedItem['BaseAmountValue'] = $item->price;
$mappedItem['Quantity'] = $item->qty;
$mappedItem['Discount'] = $item->discount;
$mappedItem['AccountNumber'] = 1000;
$mappedItem['Unit'] = 'parts';
$arr[] = $mappedItem;
}
You're fairly close to one way of doing this...
Explanation:
$arr[]['ProductGuid'] = $item->guid;
^^
\= Next numeric array key.
What this is doing is setting the productGuid key on the next numeric outer array, so in effect what you're actually setting is:
$arr[0]['ProductGuid'] = $item->guid;
$arr[1]['BaseAmountValue'] = $item->price;
$arr[2]['Quantity'] = $item->qty;
$arr[3]['Discount'] = $item->discount;
$arr[4]['AccountNumber'] = 1000;
$arr[5]['Unit'] = 'parts';
Which is clearly not what you want.
One Solution:
Therefore you will have to set the array key value on each iteration of the foreach loop.
One way of doing this is manually setting an iterator integer key value:
$arr = [];
$x = 0;
foreach($items as $item) {
$arr[$x]['ProductGuid'] = $item->guid;
$arr[$x]['BaseAmountValue'] = $item->price;
$arr[$x]['Quantity'] = $item->qty;
$arr[$x]['Discount'] = $item->discount;
$arr[$x]['AccountNumber'] = 1000;
$arr[$x]['Unit'] = 'parts';
$x++; // +1 to value of $x
}
Edit:
Nico's way of doing this is a bit neater and a bit smarter.

Create an array with indeterminate length starting on 0

I have 50 (or less) arrays from database and I need to return them in one array.
I'm currently using
$results = DB::table('coinflip_history')->where('ct', Auth::user()->steamid)->orWhere('t', Auth::user()->steamid)->orderByRaw('round_id DESC')->limit(50)->get();
$results = json_decode($results, true);
$i=1;
foreach ($results as $key => $value) {
if (!$value['winner']) $array[$i] = array('secret' => null, 'winning_string' => null, 'hash' => $value['hash'], 'timestamp' => $value['time']);
else $array[$i] = array('secret' => $value['secret'], 'winning_string' => $value['percentage'], 'hash' => $value['hash'], 'timestamp' => $value['time']);
$i++;
}
return array($array[1], $array[2], $array[3], $array[4], $array[5], $array[6], $array[7], $array[8], $array[9], $array[10], $array[11], $array[12], $array[13], $array[14], $array[15], $array[16], $array[17], $array[18], $array[19], $array[20], $array[21], $array[22], $array[23], $array[24], $array[25], $array[26], $array[27], $array[28], $array[29], $array[30], $array[31], $array[32], $array[33], $array[34], $array[35], $array[36], $array[37], $array[38], $array[39], $array[40], $array[41], $array[42], $array[43], $array[44], $array[45], $array[46], $array[47], $array[48], $array[49], $array[50]);
But if there are less than 50 arrays it's not working.
Is there any way to make it work automatically?
All arrays have indices.
It's just that kind of data data structure.
There is no way on PHP of generating an array without indices. It wouldn't be an array.
The only thing you are accomplishing with your code is generating an array starting on 1, and then creating a new array starting on 0.
Since both things are functionally equivalent, I guess that the problem exist down the line when you return an 1-based array.
So if you would do:
$array = [];
$results = json_decode($results, true);
foreach($results as $key => $value){
if(!$value['winner']) {
$array[] = [
'secret' => null,
'winning_string' => null,
'hash' => $value['hash'],
'timestamp' => $value['time']
];
}
else {
$array[] = [
'secret' => $value['secret'],
'winning_string' => $value['percentage'],
'hash' => $value['hash'],
'timestamp' => $value['time']
];
}
}
return $array;
You'd get what you need. This is 100% the same than what you are doing up there, but with less steps, and that works for any number of values on the returned $array.
// as simple as this
return $array;

An algorithm in PHP to select a subset of the first exactly N elements that add up to X threshold

This is the input:
$deals = array(
array('deal' => '1', 'deal_date' => '2017-02-13', 'amount' => '400'),
array('deal' => '2', 'deal_date' => '2017-04-17', 'amount' => '8900'),
array('deal' => '3', 'deal_date' => '2017-04-23', 'amount' => '1000'),
array('deal' => '4', 'deal_date' => '2017-06-02', 'amount' => '2400'),
array('deal' => '5', 'deal_date' => '2017-07-05', 'amount' => '10500'),
);
I am searching for a subset of exactly N elements where the sum of the 'amount' properties is greater then or equal to X and the elements have the lowest 'deal_date' property possible.
If there is no subset that fits the rules:
$subset = false;
So for N=2 and X=10000, I get this output:
$subset = array(
array('deal' => '2', 'deal_date' => '2017-04-17', 'amount' => '8900'),
array('deal' => '4', 'deal_date' => '2017-06-02', 'amount' => '2400'),
);
for N=3 and X=12000:
$subset = array(
array('deal' => '2', 'deal_date' => '2017-04-17', 'amount' => '8900'),
array('deal' => '3', 'deal_date' => '2017-04-23', 'amount' => '1000'),
array('deal' => '4', 'deal_date' => '2017-06-02', 'amount' => '2400'),
);
My current idea entails creating an array that contains arrays of the list of deals in every conceivable order. Then I scan through those for my list of deals that fit the criteria but then I have a list of deals and I am unsure how to determine the 'earliest'.
I'm also looking for the algorithm with the lowest time complexity.
Any ideas would be appreciated.
It is not simple but roughly this
There will be a better way.
The approximate source is
$calculate = 0;
$end_case = array();
$sum = 10000;
// first amount 10000 over value array remove
foreach($deals as $key => $val){
if($val['amount']>=$sum)unset($deals[$key]);
}
// second Obtaining data
foreach($deals as $key => $val){
foreach($deals as $k => $v){
// same deal excetpion
if($val['deal']==$v['deal']) continue;
$calc = deal_sum($val['amount'],$v['amount']);
if($calc>=$sum){
echo "#".$v['deal']." => ".$val['amount']."+".$v['amount']." = ".$calc."\n";
array_push($end_case,$v['deal']);
}
print_r($end_case);
}
}
function deal_sum($source,$destination){
$result = $source+$destination;
return $result;
}
You should keep in mind the time complexity of your algorithm, otherwise large input sets will take forever.
Algorithm for N:
Returns array() of the first $number_of_deals_to_combine deals from
$deals ordered by 'deal_date', that have at least $threshold or
greater combined amounts or false if no such combination is found.
Uses a rolling window of $number_of_deals_to_combine elements
excluding deals that have too small 'amount'.
function combine_deals($deals, $threshold, $number_of_deals_to_combine){
$return = false;
//are there enough deals to combine?
if(count($deals) >= $number_of_deals_to_combine){
//are deals sorted by date? sort them
usort($deals,function($a, $b){return strnatcmp($a['deal_date'],$b['deal_date']);});
//find out if there is a possible solution, by adding up the biggest deals
$amounts = array_map('intval',array_column($deals,'amount'));
rsort($amounts); //sort descending
if(array_sum(array_slice($amounts, 0, $number_of_deals_to_combine)) >= $threshold){
//find the smallest possible number that is part of a solution
$min_limit = $threshold - array_sum(array_slice($amounts, 0, $number_of_deals_to_combine - 1));
//rolling window
$combined = array();
foreach($deals as $deal){
if(intval($deal['amount']) < $min_limit){continue;} //this deal is not part of any solution
$combined[] = $deal;
//keep the number of deals constant
if(count($combined) > $number_of_deals_to_combine){
$combined = array_slice($combined, count($combined) - $number_of_deals_to_combine);
}
//there are enough deals to test
if(count($combined) == $number_of_deals_to_combine){
$amount = array_sum(array_map('intval',array_column($combined, 'amount')));
if($amount >= $threshold){
$return = $combined;
break;
}
}
}
}
}
return $return;
}
$threshold = 10000;
$number_of_deals_to_combine = 2;
$result = combine_deals($deals, $threshold, $number_of_deals_to_combine);
Are the amount fields always integers? If not, replace all intval with floatval everywhere.

adding array in PHP

First, im new in PHP so please bear with me.
I just want to add an array with the foreach loop but I can't.
if($size > 0)
{
$resultArray = array();
foreach($result as $hospital)
{
if($hospital)
{
$temp = array('result' => 'true',
'total' => $size,
'id' => $hospital['id'],
'name' => $hospital['name'],
'address' => $hospital['address'],
'telp' => $hospital['telp']);
$resultArray = array_merge_recursive($resultArray, $temp);
//$resultArray = array_splice($resultArray, $i, 0, $temp);
}
}
$this->response($resultArray, 200);
}
I tried to create a $temp array and merge it to the final result, and finally print that final result ($resultArray) to the response.
The array is successfully merged, but not in the way i want. This is the JSON result :
{"result":["true","true"],"total":[2,2],"id":["1","2"],"name":["test","keyword"],"address":["alamat test","alamat keyword"],"telp":["123456","789"]}
What i want is something like this :
-0 :{
result: "true"
total: 2
id: "1"
name: "test"
address: "alamat test"
telp: "123456"
}
-1 :{
result: "true"
total: 2
id: "2"
name: "test2"
address: "alamat tes 2t"
telp: "789"
}
So the response should be an array with 2 items, and each items has some items.
Please kindly help me, Thanks for your help.
It looks to me like you're trying to append an array, to an array, which can be done quite easily:
$resultArray = array();//outside the loop
//loop
$resultArray[] = array(
'result' => 'true',
'total' => $size,
'id' => $hospital['id'],
'name' => $hospital['name'],
'address' => $hospital['address'],
'telp' => $hospital['telp']
);
Job done. You could also use the array_push function, but that's a function. Functions equal overhead, and should, therefore be avoided (if it doesn't affect code readability). You use array_merge and array_merge_recursive if you want to combine arrays:
$foo = array(
array('bar' => 'zar')
);
$bar = array(
array('car' => 'far')
);
var_dump(
array_merge(
$foo,
$bar
)
);
The effect of array_merge here is comparable to:
foreach ($bar as $subArray)
$foo[] = $subArray;
Im not totally sure how you want your final array to be, but this will put it like $resultArray[0] = array( //temparray ) etc..:
if($size > 0){
$resultArray = array();
$i=0;
foreach($result as $hospital){
if($hospital){
$temp = array('result' => 'true',
'total' => $size,
'id' => $hospital['id'],
'name' => $hospital['name'],
'address' => $hospital['address'],
'telp' => $hospital['telp']);
$resultArray[$i][$temp];
$i++;
}
}
$this->response($resultArray, 200);
}
Replace this -
$resultArray = array_merge_recursive($resultArray, $temp);
With this -
$resultArray[] = $temp;

aray_combine not working as expected

I expect array_combine to be able to take two arrays such as:
array('sample', 'sample_two', 'sample');
array(array('info', 'info_two'), array('bananas'), array('more_stuff'));
and produce:
array(
'sample' => array(
'info', 'info_two', 'more_stuff'
),
'sample_two' => array(
'bananas'
)
);
instead I get:
array(
'sample' => array(
'more_stuff'
),
'sample_two' => array(
'bananas'
)
);
Now I know php doesn't allow for duplicate key's so how can I use array_combine or some other method to achieve the desired array? It is safe to say that, the first array, will always match the second array in terms of layout. So you can draw lines between the values of array one and array two.
Why not writing your own function?
function my_array_combine(array $keys, array $values) {
$result = array();
for ($i = 0; $i < count($keys); $i++) {
$key = $keys[$i];
if (!isset($result[$key])) {
$result[$key] = array();
}
$result[$key] = array_merge($result[$key], $values[$i]);
}
return $result;
}

Categories