Convert multi-dimensional array into matrix-like array - php

I have the following array:
$array = array(
array("2018","2019"),
"Jan",
array("France","Germany")
);
I need a matrix that crosses all the elements of the array; e.g:
array(
array("2018","Jan","France"),
array("2018","Jan","Germany"),
array("2019","Jan","France"),
array("2019","Jan","Germany")
);
meaning, 2 x 2 x 1 arrays
but this can be that I have more elements that are or are not arrays then:
$array = array(
array("2018","2019"),
"Jan",
array("France","Germany"),
array("prod1","prod2","prod3"),
'Act'
);
In this case I would get 2 x 2 x 1 x 4 x 1 arrays in the end.
Any idea on how to achieve this?

Is that what you are looking for ?
$dst = array(array());
foreach ($array as $idx => $val) {
foreach ($dst as $tmp_idx => $tmp_array) {
if (is_array($val)) {
foreach ($val as $sub_idx => $sub_val) {
$dst[] = array_merge($dst[$tmp_idx], array(count($dst[$tmp_idx]) => $sub_val));
}
} else {
$dst[] = array_merge($dst[$tmp_idx], array(count($dst[$tmp_idx]) => $val));
}
unset($dst[$tmp_idx]);
}
}
I declare the array with an empty array in it.
A first foreach iterates through the main array to access all the categories, whatever their number.
A second foreach iterates through the result array.
A third foreach - or not, depending if the category contains several or a single entry - iterates through each entry of the current category and merges an array containing only that current category in the result currently iterated on (from $dst). The merge is pushed into the result array. The result stored in $dst are assumed to be temporary and will be copied and completed with each new value, making them progressively contain one entry from each category.
At the end of each iteration on $dst, the current temporary result is removed from the array since it has no purpose : if the foreach iterated on it, that means that it was incomplete.
At the end, the only entries in $dst that remain are these who weren't iterated on, meaning that every entry from each category were considered when they were created, meaning that they are complete.
I'm available for any clarification may the need arise.

Related

Restructure 2d array so that column values become row values (transpose but preserve first level keys)

The situation is as follows. I have a parent array which looks like the following:
$parent = [
1 => ['test1', 'test2'],
2 => ['test1_1', 'test2_2'],
];
I would like to group the data by column.
Desired result:
[
1 => ['test1', 'test1_1'],
2 => ['test2', 'test2_2'],
]
1 parent array called parent contains 2 arrays inside. I want to combine these two so that they have the same values as stated above. So this would mean that the arrays should be combined based on index number.
Since I do not make use of string keys, how would I accomplish this? I believe that there is no build in function available for this situation.
I would imagine that I could start beginning to create a new array and use a for loop through the parent array.
I tried the array-combine function however, this is NOT displaying the results I want.
[
1 => ['test1' => 'test1_1', 'test2' => 'test2_2'
]
If you need to preserve those first level keys, you can re-apply them after tranposing.
Code: (Demo)
var_export(
array_combine(array_keys($parent), array_map(null, ...$parent))
);
Otherwise, you can just transpose and accept the re-indexed first level keys. Honestly, I can't see any good reason to preserve the first level keys because by transposing, you remove the initial association between first level keys and the row values.
Code: (Demo)
var_export(
array_map(null, ...$parent)
);
If these techniques do not suit your actual project data, then we will need a more realistic sample array to be provided in your question body.
Loop over the keys of the top-level array. Then use the current index of the iteration to get the corresponding columns of the nested arrays.
$result = [];
foreach (array_keys($parent) as $i => $k) {
$result[$k] = array_column($parent, $i);
}
DEMO
This assumes the number of rows is the same as the number of columns. It's not clear what you expect the result to be if that's not true.

How to pass data from a master array to multiple arrays in PHP

I have an array with subarrays, each subarray has 'type' (int) and a 'datetime'. I need to loop trought the array and create new arrays (or objects) with groups of these subarrays in a special way: every 'type' could be [0-3] 0 means startdate and 3 means enddate (1 and 2 are in between for other purposes). Every new array with these subarrays should start with 0 and end with 3.
Im not using any framework, only PHP5 and jQuery. The master array comes from a loop I made from a SQL query with GROUP_CONCAT. I just passed the concat fields to a master array but I need to regroup the subarrays to create some type of registries.
Here is the array I got with subarrays. They are already sorted by datetime, every type '0' means "start date / start new registry" and '3' means "end date / end registry".
$array = [
["tipo"=>0,"fechahora"=>"2019-05-26 09:35:30"],
["tipo"=>1,"fechahora"=>"2019-05-26 10:30:15"],
["tipo"=>2,"fechahora"=>"2019-05-26 10:43:12"],
["tipo"=>3,"fechahora"=>"2019-05-26 14:30:26"],
["tipo"=>0,"fechahora"=>"2019-05-26 15:35:22"],
["tipo"=>3,"fechahora"=>"2019-05-26 16:35:31"],
["tipo"=>0,"fechahora"=>"2019-05-27 08:31:57"],
["tipo"=>1,"fechahora"=>"2019-05-27 10:27:22"],
["tipo"=>2,"fechahora"=>"2019-05-27 10:38:31"],
["tipo"=>3,"fechahora"=>"2019-05-27 14:20:38"],
["tipo"=>0,"fechahora"=>"2019-05-28 09:39:42"],
["tipo"=>1,"fechahora"=>"2019-05-28 11:43:08"],
["tipo"=>2,"fechahora"=>"2019-05-28 11:53:19"],
["tipo"=>3,"fechahora"=>"2019-05-28 14:43:31"],
["tipo"=>0,"fechahora"=>"2019-05-29 10:30:22"],
["tipo"=>3,"fechahora"=>"2019-05-29 14:38:46"]
];
I need to create a new array with subarrays or objects by registry, a registry is an element who have startdate (type 0), somedates in between (type 1,2) and a enddate (type 3). Its important The next subarray have the startdate more current than the older subarray enddate. Every "tipo"+"fechahora" array have more fields (signature, address, etc) so I need to keep these as subarrays:
$newarray = [
'registry0' => [
"startdate"=> ["tipo"=>0,"fechahora"=>"2019-05-26 09:35:30"],
"pauses"=> [
["tipo"=>1,"fechahora"=>"2019-05-26 10:30:15"],
["tipo"=>2,"fechahora"=>"2019-05-26 10:43:12"]
],
"enddate" => ["tipo"=>3,"fechahora"=>"2019-05-26 14:30:26"]
],
'registry1' => [
"startdate"=> ["tipo"=>0,"fechahora"=>"2019-05-26 15:35:22"],
"pauses"=> [],
"enddate" => ["tipo"=>3,"fechahora"=>"2019-05-26 16:35:31"]
],
'registry2' => [
"startdate"=> ["tipo"=>0,"fechahora"=>"2019-05-27 08:31:57"],
"pauses"=> [
["tipo"=>1,"fechahora"=>"2019-05-27 10:27:22"],
["tipo"=>2,"fechahora"=>"2019-05-27 10:38:31"]
],
"enddate" => ["tipo"=>3,"fechahora"=>"2019-05-27 14:20:38"]
]
];
I could use arrays or objects, I dont know hot to develop a loop to regroup arrays like this (starting at one field value and ending with another and so on). I dont even know if there is a simpler way. Any help would be appreciated.
If this could help: I need the registries this way to put into an HTML table, each registry in a row.
EDIT:
Iknow I have to use a loop but I dont know how to "get first element with type 0, create an array, include element, include all other element until type 3, close array, create new array and so on..."
In some code:
$newarray = [];
foreach($array as $element) {
if ($element["tipo"]==0) {
//new subarray
//include this in subarray
}
// include all "tipos" 1,2 in subarray
if ($element["tipo"]==3) {
//include this in subarray
//finish subarray
}
//incude subarray in $newarray
}
return $newarray;
I dont know how to continue.
For this case a simple foreach followed by switch case will do,
$result = [];
$i = 0;
$var = 3; // you can manage this at your will
foreach ($array as $key => $value) {
switch ($value['tipo']) { // passing tipo value here
case 0:
// it will capture for same record for current value of $i
$result['registry' . $i]['startdate'] = $value;
break;
case $var:
// detects last entry and will increment
$result['registry' . $i++]['enddate'] = $value;
break;
default:
// if not 0 and $var then push it in pauses
$result['registry' . $i]['pauses'][] = $value;
break;
}
}
Demo Link.
It is just a case of looping over each of the original array values and creating an entry in a temporary array ($temp in this case) with any values according to type. Then when a type 3 is found, adding this to the $newarray...
$newarray = [];
$temp = [];
$count = 0;
foreach ( $array as $entry ) {
if ( $entry['tipo'] == 0 ) {
$temp = [ 'startdate' => $entry ];
}
else if ( $entry['tipo'] == 3 ) {
$temp["enddate"] = $entry;
$newarray["registry".$count++] = $temp;
}
else {
$temp["pauses"][] = $entry;
}
}

Sort an associative array by using another array's ordered key-value association

Finding the right title for this was next to impossible.
Imagine this scenario:
We have an array that contains certain product tags. The key is each tag's unique id and the value is its label:
Available Tags
Array (
[3] => Sweet
[4] => Sour
[5] => Bitter
[6] => Winter
[7] => Organic
)
We have another array which contains the tags that have been selected. The selection has a specific order which is defined by the key, while the value represents the id (of the actual tag we see in array #1).
Selected Tags in Specific Order
Array (
[10] => 4
[20] => 3
[30] => 7
)
My theoretical Approach
Certainly i could go about foreach-ing through the second array, collecting the appropriate values (that correspond to the first array's entries) in a new array. Then i could iterate over the first array and add all the values (to the new array) which are not yet present in the new array.
Quite honestly - that doesn't feel very professional. Unfortunately, i have no idea how to do this better.
Question
How can i neatly sort the first array (Available Tags) by using the chronology defined by the second array (Selected Tags)?
Note
I want to end up with all items from the first array. Not just the ones that are listed in the second one.
In case someone's curious: this is for multiple-selects which are sortable. Items which have been selected are sortable and must therefore appear in the right order. The other items order doesn't matter. My server-side data handler class gives me these two arrays as described, so that's what i got to work with.
Here's a solution that uses uksort(). Elements of the $tags array that are not present in the $order array are sorted to the end, and the relative order between them is undefined.
function my_sort($a, $b) {
global $order;
if(in_array($a, $order)) {
if(in_array($b, $order)) {
// Both $a and $b have an order
return array_search($a, $order) - array_search($b, $order);
}
else {
// Only $a has an order, so it goes before $b
return -1;
}
}
else if(in_array($b, $order)) {
// Only $b has an order, so it goes before $a
return 1;
}
else {
// Neither $a or $b has an order, so we don't care how they're sorted
return 0;
}
}
uksort($tags, 'my_sort');
I think you can just loop in your second array and build a new one using keys
$new = array();
foreach($array2 as $key => $val)
{
$new_array[] = $array1[$val];
}
Now the selected items are ordered in your $new_array
Sample

Fast method for Looping through Array to match a common ID in a second array

I'm trying to systematically loop through 2 arrays, and match their values for some quick processing. Let me set up my specific situation:
Array 1
productID,
companyID,
name,
price
Array 2
companyID,
name,
rebate1,
rebate2
I want to loop through Array 1, and when the companyID matches an ID inside of Array 2, I will do some quick math based on the rebate1, rebate2 values for that companyID.
Right now I am looping through Array 1, and then on EACH item in Array 1 I loop through the entire Array 2 to see if the companyID exists. I know this can't be the fastest/cleanest solution...
EDIT
The key values for Array 1 look like:
$array1[0]['productID']
$array1[0]['companyID'] (etc...)
$array1[1]['productID']
$array1[1]['companyID'] (etc...)
The key values for Array 2 look like:
$array2[0]['companyID']
$array2[0]['rebate1'] (etc...)
$array2[1]['companyID']
$array2[1]['rebate1'] (etc...)
Use the companyId as key for Array 2, i.e., make sure that
$Array2[$Array1[$i]['companyID']]['companyID'] == $Array1[$i]['companyID']
This gives you constant time lookup of companies in Array 2 based on companyID and you can do your calculation with
$Array2[$Array1[$i]['companyID']]['rebate1']`
and
$Array2[$Array1[$i]['companyID']]['rebate2']`
Example:
foreach ($Array1 as $value) {
if (isset($Array2[$value['companyID']])) {
//TODO: use $Array2[$value['companyID']] for calculation
} else {
// TODO: handle case companyID not in $Array2
}
}
What approximate sizes do you expect for each of your arrays?
While as you say, your method isn't certainly the fastest (looks like 0(n²)), below 10'000 elements in each array I doubt you can see any significant speed difference.
If you have 150'000 in array1 and 200'000 in array2, that's a whole other story and you'll have to look for an algorithm that is rather 0(log n).
Edit:
As mentioned above, let's just make your array associative if you can:
Instead of:
Array2 = array(
0 => array(
'Company_id' => 'Hello',
'rebate_1' => '50%',
)
);
Make it:
Array2 = array(
'Hello' => array(
'rebate_1' => '50%',
)
);
And then use:
if (isset(array2[$company_id]))
{
// do your stuff
}
If you can't modify Array2's structure where it's coming from, you should transform it on the fly in your search function's scope, so that it looks like above, and then use the transformed array. Transforming Array2 into an associative one shouldn't take too long, for the number of elements you say you have.
The easiest way I can think of is to use the companyID as the key for Array 2:
$companies[$companyID]=array($name,$rebate1,$rebate2)
and then you just reference it directly looping Array 1
$products[$x]=array($productID,$companyID,$name,$price);
...
$newprice1=$products[$x][3]/$companies[$products[$x][1]][1];
$newprice2=$products[$x][3]/$companies[$products[$x][1]][2];
My answer is slightly different from the first one, mind the arrays...
You can create a new array indexed by the company id as follows:
$cid = array_map(function($a) {return $a['companyID'];}, $array2);
$new2 = array_combine($cid, $array2);
With that, you can loop through the first array and access the data:
foreach ($array1 as $key => $value){
$rebate1 = $new2[$value['companyID']]['rebate1'];
$rebate2 = $new2[$value['companyID']]['rebate2'];
}

PHP: Session 2-Dimensional Array - Track Viewed Products

I'm trying to create an array to display the last 5 products a customer has viewed.
The array is a 2 dimensional array like below...
$RView= array(
array( ID => "1001", RefCode => "Ref_01", Name => "Name_01" ),
...
array( ID => "1005", RefCode => "Ref_05", Name => "Name_05" )
);
The array values are retrieved from the products recordset and is designed to function as follows when a customer visits a product page.
Page will check if a Session Array exists
If yes, an array variable is created from existing Session
If no, a new array is created.
Array will add the new product details.
Array will count if there are more than 5 existing products in the array.
If yes, it will remove the oldest.
If no, moves to next step.
A Session is created/updated from the revised Array.
My current effort is attached below...
Many thanks for any help.
<?php
session_start()
// Get or Create Array
IF (isset($_SESSION['sessRView'])) {
$RView = ($_SESSION['sessRView']); }
ELSE {
$RView = array(array());
}
// Append currently viewed Product to Array
array(array_unshift($RView, $row_rsPrd['PrdID'], $row_rsPrd['RefCode'], $row_rsPrd['Name']));
// Check if more than 5 products exist in Array, if so delete.
IF (sizeof($RView) > 5) {
array(array_pop($RView)); }
// Update Session for next page
$_SESSION['sessRView'] = $RView;
// Display Array
for ($row = 0; $row < 5; $row++)
{
echo "<ul>";
echo "<li><a href='?PrdID=".$RView[$row]["PrdID"]."'>".$RView[$row]["RefCode"]."</a> : ".$RView[$row]["Name"]."</li>";
echo "</ul>";
}
?>
It's more or less right - just 2 lines need to be changed.
There's no need for the extra array() around array_unshift and array_pop.
When you use array_unshift you're pushing an array of items (not the id/codes individually) - I think you mean array_unshift($RView, array($prodid,$name,...))
What if $RView doesn't have 5 elements? In that case you're accessing undefined array indices (which may or may not show an error). Change it to a foreach loop: e.g.
foreach ($Rview as $prod) echo $prod['Name']...
It should work after you make these changes. You might want to clean up the coding style a bit, though :)
EDIT: Oh, I see, when you're referencing the array in the for loop it doesn't know that the array has "ProdID" and "Name" indices. When you make an array you have to define the indexes using the => operator.
Add indexes to the array when you array_unshift:
array_unshift($RView, array("ProdID" => $row_rsProd["ProdID"], "Name"...))
If row_rsProd isn't too big, you can just tack the entire row_rsprod onto $RView.
so change array_unshift(...) to just $RView[] = $row_rsProd
This way the indexes are preserved.
Alternatively you can change the indicies in the for loop to match. Right now the array you unshift onto $RView is 0-based - $RView[0][0] is the product ID for the first product, etc.
So you can change the stuff in the foreach loop to
echo "<li>..." $prod[0] $prod[1] $prod[2]
Hope that helps!

Categories