Moving multidimensional array items - php

So i have this array:
$input = array (
1 => array (
'TitleName' => 'Details',
'TitleID' => 1,
1 => array (
'ID' => 1,
'Name' => 'First Name'
),
2 => array (
'ID' => 2,
'Name' => 'Last Name'
),
3 => array (
'ID' => 4,
'Name' => 'City')
),
12 => array (
'TitleName' => 'System',
'TitleID' => 12,
0 => array (
'ID' => 3,
'Name' => 'Cpu'
)
)
);
And i have an Array that tells me how to order the array above:
$order = array
(
1 => array(
0 => 1, // this is the ID in the third dimension
1 => 4,
2 => 2,
),
12 => array (
0 => 3
)
);
So the point is that i will get in my final array :
Array
(
[1] => Array
(
[TitleName] => Details
[TitleID] => 1
[1] => Array
(
[ID] => 1
[Name] => First Name
)
[2] => Array
(
[ID] => 4
[Name] => City
)
[3] => Array
(
[ID] => 2
[Name] => Last Name
)
)
[12] => Array
(
[TitleName] => System
[TitleID] => 12
[0] => Array
(
[ID] => 3
[Name] => Cpu
)
)
)
Also, how can i move items inside the array to different parent?
I've tried this code, but no luck.
usort($array, function ($a, $b) use ($order) {
$pos_a = array_search($a['id'], $order);
$pos_b = array_search($b['id'], $order);
return $pos_a - $pos_b;
});
Any ideas?? Thanks!

Since your id's are unique, it might be easier to just fill two temporary arrays and then iterate over the ordering array to create your desired output.
Here's a possible solution. No need for array_search here.
Having your two goven arrays, we'll first iterate over the input, seperating the common 1st level elements and it's attributes and the children elements.
To distinguish between attributes of first level elements and chiuldren we use is_numeric on the key (since attribute keys are not numeric) and is_array (just to be sure).
// our temporary arrays
$tmpElements = array();
$tmpChildren = array();
// iterate over array
foreach($input as $key => $value) {
$tmpElementAttributes = array(); // init/reset the temporary attributes array
// iterate over children and attributes
foreach ($value as $subKey => $subValue) {
// if the value is an array and the key is numeric, it is a child element
if(is_array($subValue) && is_numeric($subKey)) {
$tmpChildrenKey = $subValue['ID'];
$tmpChildren[$tmpChildrenKey] = $subValue;
}
else { // otherwise it is an attribute
$tmpElementAttributes[$subKey] = $subValue;
}
}
$tmpElements[$key] = $tmpElementAttributes; // add the gathered attributes that define our firstLevel Element
}
So now we have two arrays. One ($tmpElements) has all the first Level Elements (Details, Systems) and the other one ($tmpChildren) has all the child Elements (First Name, Last Name, Cpu, City). For both arrays we took their id's as array key.
Now we iterate over the ordering array, filling in our child elements into the respective first level elements in our $tmpElements Array accoring to $order.
foreach($order as $key => $values) {
foreach($values as $orderId) {
$tmpElements[$key][] = $tmpChildren[$orderId];
}
}
Here, $key from the first array is the array key for our first level element, both in order as in your source/input and as in our $tmpElements. So we can use it to identify elements in our array.
The $orderId from the second foreach is the second level children element ID.
So we use it to reach our respective Children Element.
Hence: $tmpElements[$key][] = $tmpChildren[$orderId];

Related

Send a variable number of POST array variables to their own array

I've got a dynamic form that allows a user to create as many form elements as they need -- then submit them. For this, I have prepared the input names as arrays like
<input name="title[]" ...
and posting them gives me output like
Array
(
[day] => 0
[project_id] => 5
[submit] => publish
[id] => Array
(
[0] => 4
[1] => 5
)
[title] => Array
(
[0] => Step 1
[1] => Step 2
)
[max_vol] => Array
(
[0] => 2
[1] => 3
)
[description] => Array
(
[0] => df dofi dofidfoi
[1] => dfvpdofvdpfo osd pod
)
)
I've created something that allows me to just grab the post arrays like so
foreach( $_POST as $post_key ) {
// ignore non-array post variables
if( is_array( $post_key ) ) {
foreach( $post_key as $form_value ) {
echo "$form_value\n";
}
}
}
/* ouputs...
4
5
Step 1
Step 2
2
3
df dofi dofidfoi
dfvpdofvdpfo osd pod
*/
which nicely sorts the non-arrays from the arrays, but I can't figure out how to take this variable number of created form elements and prepare them into an array variable that looks something like...
Array
(
[0] => Array
(
'id' => 4, 'title' => 'Step 1', 'max_vol' => '2', 'description' => 'df dofi dofidfoi'
),
[1] => Array
(
'id' => 5, 'title' => 'Step 2', 'max_vol' => '3', 'description' => 'dfvpdofvdpfo osd pod'
),
// could be more or less elements...
);
(I will be eventually passing these arrays to a MySQL query builder function).
Thanks.
How about creating a variable that is outside the scope of the foreach loop
$results = array();
foreach( $_POST as $post_key=>$post_value ) {
// ignore non-array post variables
if( is_array( $post_value ) ) {
foreach( $post_value as $form_key=>$form_value ) {
if (!isset($results[$form_key]))
{
$results[$form_key] = array();
}
$results[$form_key][$post_key] = $form_value;
}
}
}
// results is your array variable
print_r($results);
Iterate over some significant $_POST-array key, for example - id and get the values from other $_POST-arrays with the same index:
$values = array();
foreach ($_POST['id'] as $k => $v) {
$values[] = array(
'id' => $v,
'title' => $_POST['title'][$k],
'max_vol' => $_POST['max_vol'][$k],
'description' => $_POST['description'][$k],
);
}
print_r($values);

Pushing a sub array into the same array

I am trying to put content of one array into the same array. Here I have an array $mclass with values such as
Array
(
[0] => stdClass Object
(
[room_id] => 1,3,5
[day] => 1
[class_teacher] => TEA-2014-2
[final_exam_date] => 2015-09-21
)
)
You can see I have room_id index with 1,3,5 value. Now, I want to explode the room_id and get duplicate of same array index data with change of room_id and push into the array. and finally delete the current array index such as [0]. Here I want the final result as.
Array
(
[0] => stdClass Object
(
[room_id] => 1
[day] => 1
[class_teacher] => TEA-2014-2
[final_exam_date] => 2015-09-21
)
[1] => stdClass Object
(
[room_id] => 3
[day] => 1
[class_teacher] => TEA-2014-2
[final_exam_date] => 2015-09-21
)
[2] => stdClass Object
(
[room_id] => 5
[day] => 1
[class_teacher] => TEA-2014-2
[final_exam_date] => 2015-09-21
)
)
Here is my code for the same:
if(count($mclass)>0)
{
foreach($mclass as $mclasskey=>$mclass_row)
{
/* Room ID Calculation */
if(isset($mclass[$mclasskey]))
{
$temp_room_id = explode(',',$mclass_row->room_id);
if(count($temp_room_id)>1)
{
foreach($temp_room_id as $trkey=>$tr)
{
if(!in_array($temp_room_id[$trkey], $morning_class_semester))
{
array_push($morning_class_semester,$temp_room_id[$trkey]);
}
}
if(count($morning_class_semester)>0)
{
foreach($morning_class_semester as $mcskey=>$mcs)
{
$index_count = count($new_test);
$test[$index_count] = $mclass[$mclasskey];
$test[$index_count]->room_id = $morning_class_semester[$mcskey];
array_push($new_test,$test[$index_count]);
}
unset($mclass[$mclasskey]);
}
}
}
}
}
The code below does what you're looking for using only arrays. So you'll have to change the array access operators to -> since you're accessing an object. I'd do so, but it would break the example, so I'll leave that up to you.
Code Explained:
Loop through array selecting each subarray (object in your case), explode on the $item('room_id') ... ($item->room_id in your case) ... and create sub arrays, via loop, from that using the data from the original using each key. Remove the original item (which has the combined room_ids) and combine the placeholder and original array.
<?php
//Establish some data to work with
$array = array(
array(
"room_id" => "1,3,5",
"day" => 1,
"class_teacher" => "TEA-2014-2",
"final_exam_date" => "2015-09-21",
));
foreach ($array as $key => $item) {
$placeholder = array();
$ids = explode(',',$item['room_id']);
if (count($ids) > 1) {
foreach ($ids as $id) {
$push = array(
'room_id' => $id,
'day' => $item['day'],
'class_teacher' => $item['class_teacher'],
'final_exam_date' => $item['final_exam_date']
);
array_push($placeholder, $push);
}
$array = array_merge($array, $placeholder);
unset($array[$key]);
}
}
var_dump($array);
?>

2 arrays: keep only elements with different value

I have 2 arrays:
Array
(
[0] => Array
(
[id] => 1
[fieldname] => banana
[value] => yellow
)
)
Array
(
[0] => Array
(
[id] => 1
[fieldname] => rome
[value] => city
)
[1] => Array
(
[id] => 2
[fieldname] => bla
[value] => yes
)
)
I want to create a new array that contains only elements where "id" is different. In other words I want to get this output:
Array
(
[0] => Array
(
[id] => 2
[fieldname] => bla
[value] => yes
)
)
[id] => 2 was the only different [id] so I keep it.
Said that I've already managed to achieve my goal with an inefficient pile of foreach, if statements and temp variables. I really don't want to use a wall of code for this very small thing so I started to look for a native PHP function with no success. What's the easiest way to get the result? Is it possible that I strictly need to use a foreach with so many if?
You can use array_udiff with a function.
Computes the difference of arrays by using a callback function for
data comparison.
Returns an array containing all the values of the first array that are not
present in any of the other arguments.
The code:
// Setup two arrays as per your question
$array1 = array (
'0' => array (
'id' => '1',
'fieldname' => 'banana',
'value' => 'yellow',
)
);
$array2 = array (
'0' => array (
'id' => '1',
'fieldname' => 'rome',
'value' => 'city',
),
'1' => array (
'id' => '2',
'fieldname' => 'bla',
'value' => 'yes',
)
);
// Setup the callback comparison function
function arrayCompare($array2, $array1) {
return $array2['id'] - $array1['id'];
}
// Use array_udiff() with the two arrays and the callback function
$arrayDiff = array_udiff($array2, $array1, 'arrayCompare');
print_r($arrayDiff);
The above code returns the following:
Array (
[1] => Array (
[id] => 2
[fieldname] => bla
[value] => yes
)
)
This should do it. Not super short and it does use a temporary variable, so perhaps not what you were looking for. I've named the two arrays one and two.
$ids = array();
$result = array();
foreach ($one as $x) {
$ids[$x['id']] = 1; //This way, isset($x['id']) vill return true
}
foreach ($two as $x) {
if (!isset($ids[$x['id']])) {
$result[] = $x;
}
}
I would be surprised if there wasn't an even more compact way to do it.
EDIT: This is an alternative variant with nested for each. Not particularly short either.
$result = array();
foreach ($one as $x) {
foreach ($two as $y) {
if ($x['id'] == $y['id']) {
//A match, lets try the next $x
continue 2;
}
}
//No matching id in $two
$result[] = $x;
}

Move specific array items to beginning of array without altering order of the rest

I have an array:
Array
(
[product1] => Array
(
[id] => 1
[title] => 'p1'
[extra] => Array(
[date] => '1990-02-04 16:40:26'
)
)
[product2] => Array
(
[id] => 2
[title] => 'p2'
[extra] => Array(
[date] => '1980-01-04 16:40:26'
)
)
[product3] => Array
(
[id] => 3
[title] => 'p3'
[extra] => Array(
[date] => '2000-01-04 16:40:26'
)
)
[product4] => Array
(
[id] => 4
[title] => 'p4'
[extra] => Array(
[date] => '1995-01-04 16:40:26'
)
)
[product5] => Array
(
[id] => 5
[title] => 'p5'
[extra] => Array(
[date] => '1960-01-04 16:40:26'
)
)
...
I need to get 2 products with the latest date and move them to the start of the array.
I've looked into the multisort function, and I could sort the array like this, but then the entire array would be arranged by date, I want to maintain the order of the array but just bump up the latest 2 rows.
I need to pick out the 2 latest (order by date) from the array, then move these to the start of the array. So the order of the ids should be:
3,4,1,2,5
The latest 2 have been moved to the front of the array, the remainder are still ordered by id.
Not the most optimal implementation, but the most straight forward:
$array = /* your data */;
$latest = $array;
uasort($latest, function (array $a, array $b) {
return strtotime($a['extra']['date']) - strtotime($b['extra']['date']);
});
array_splice($latest, 2);
$latestOnTop = array_merge($latest, array_diff_key($array, $latest));
The array_splice operation requires that your array keys are actually product1 or similar; won't work with numeric indices, as they'll be renumbered. Use another truncation mechanism if that's the case.
If your array is really big, a complete sort will be unnecessarily slow. In that case, you should rather loop over the array once, keeping track of the two latest items (and their keys) you could find, then array_diff_key and array_merge on that. That's a bit more difficult to implement (left as exercise for the reader), but much more efficient.
// Making array with only dates
$dates = array();
foreach ($arr as $key => $item)
$dates[$key] = $item['extra']['date'];
// Sort it by date saving keys
uasort($dates, function($i1, $i2) { return strtotime($i1) - strtotime($i2); });
// Take keys
$dates = array_keys($dates);
// Create array with two needed items
$newarray = array( $dates[0] => $arr[$dates[0]], $dates[1] => $arr[$dates[1]]);
// remove these items
unset($arr[$dates[0]]); unset($arr[$dates[1]]);
// put them in array start
$arr = array_merge($newarray, $arr);
var_dump($arr);
// copy current array for new array
$temp = $input;
// sort temp array by latest date
uasort($temp, function($a,$b) {
return (strtotime($a['extra']['date']) < strtotime($b['extra']['date']));
});
// for 2 key value pairs to get on top
$sorted_keys = array_keys($temp);
// initialize your required array
$final = [];
// two keys to move on top
$final [ $sorted_keys[0] ] = $temp [ $sorted_keys[0] ];
$final [ $sorted_keys[1] ] = $temp [ $sorted_keys[1] ];
foreach ($input as $k => $v)
{
// insert your other array values except two latest
if(!array_key_exists($k, $final))
{
$final[$k]=$v;
}
}
unset($temp); // free up resource
$final is your required array

Merge Same Values Array in to One (string value) and sum its corresponding integer value in to same Index?

I have got this array
Array
(
[0] => Array
(
[category_name] => Dessert1
[totalOrders] => 3
)
[1] => Array
(
[category_name] => Dessert1
[totalOrders] => 1
)
[2] => Array
(
[category_name] => Category 3
[totalOrders] => 1
)
)
and I want to convert it into this array
Array
(
[0] => Array
(
[category_name] => Dessert1
[totalOrders] => 4
)
[1] => Array
(
[category_name] => Category 3
[totalOrders] => 1
)
)
It is really rather simple. You just loop over your data and pick out the unique categories. When there are duplicates add the orders to the category's total.
// The stuff from your post
$data = array(
array('category_name' => 'Dessert1', 'totalOrders' => 3),
array('category_name' => 'Dessert1', 'totalOrders' => 1),
array('category_name' => 'Category 3', 'totalOrders' => 1),
);
// Auxiliary variable
$result = array();
// Go over the data one by one
foreach ($data as $item)
{
// Use the category name to identify unique categories
$name = $item['category_name'];
// If the category appears in the auxiliary variable
if (isset($result[$name]))
{
// Then add the orders total to it
$result[$name]['totalOrders'] += $item['totalOrders'];
}
else // Otherwise
{
// Add the category to the auxiliary variable
$result[$name] = $item;
}
}
// Get the values from the auxiliary variable and override the
// old $data array. This is not strictly necessary, but if you
// want the indices to be numeric and in order then do this.
$data = array_values($result);
// Take a look at the result
var_dump($data);

Categories