Hi guys I am a beginner with PHP and want the best way in terms of performance to convert this array:
$old = array(
20 =>
array(
'name' => 'Heels',
'path' => '1/2/10/15/20',
),
15 =>
array(
'name' => 'Sandals',
'path' => '1/2/80/96/15',
),
10 =>
array(
'name' => 'Trainers',
'path' => '1/2/80/96/10',
),
);
To this:
$new = array(
20 =>
array(
'value' => 20,
'label' => 'Trainers > Sandals > Heels',
),
);
There is going to be loads of records surely exploding the paths and mapping them with the ids is going to slow it down in terms of performance just wondering whether there is a more efficient way if possible thanks.
If I understand correctly, you're trying to get the latest path relevant to each category and output it as a breadcrumb.
You can first sort the keys (ids) and then loop through the array creating the breadcrumb.
arsort($paths); # This gives the desired output in OP but makes more sense to use krsort() to sort DESC not ASC
$breadcrumb = (object) array (
'value' => array_keys($paths)[count($paths) - 1], # Get the highest id - if using krsort() use array_keys($paths)[0]
'labels' => implode(' > ', array_column($paths, 'name'));
);
# Update derived from The fourth bird's answer which skips the need for the foreach().
# Concept is to build an array of the labels to then make look pretty with the > effect
Here is a demo.
Output:
object (stdClass) (2) {
["value"] => int(20)
["labels"] => string(26) "Trainers > Sandals > Heels"
}
Another option could be to first create a mapper of the keys and the names. Then you could take the key from the mapper to create the path:
$result = [];
$mapper = array_combine(array_keys($old), array_column($old, 'name'));
foreach ($old as $key => $value) {
$path = implode(' > ', array_map(function($x) use ($mapper) {
return $mapper[(int)$x];
}, explode('/', $value['path'])));
$result[$key] = ['value' => $key,'label' => $path];
}
print_r($result);
Php demo
This is the hardcoded way, but i think you need to give a bit more information to get a dynamic solution.
<?php
$old = array(
20 =>
array(
'name' => 'Heels',
'path' => '1/2/10/15/20',
),
15 =>
array(
'name' => 'Sandals',
'path' => '1/2/80/96/15',
),
10 =>
array(
'name' => 'Trainers',
'path' => '1/2/80/96/10',
),
);
ksort($old);
$breadcrumbs = [];
$currentKey = 0;
foreach ( $old as $itemKey => $item) {
$currentKey = $itemKey;
$breadcrumbs[] = $item;
}
$new = [$currentKey] = [
'value' => $currentKey,
'label' => implode(' > ', $breadcrumbs)
];
printf($new);
Related
I have an array of arrays, as such
$statuses = array(
[0] => array('id'=>10, 'status' => 'active'),
[1] => array('id'=>11, 'status' => 'closed'),
[2] => array('id'=>12, 'status' => 'active'),
[3] => array('id'=>13, 'status' => 'stopped'),
)
I want to be able to make a new array of arrays and each of those sub arrays would contain the elements based on if they had the same status.
The trick here is, I do not want to do a case check based on hard coded status names as they can be random. I want to basically do a dynamic comparison, and say "if you are unique, then create a new array and stick yourself in there, if an array already exists with the same status than stick me in there instead". A sample result could look something like this.
Ive really had a challenge with this because the only way I can think to do it is check every single element against every other single element, and if unique than create a new array. This gets out of control fast if the original array is larger than 100. There must be some built in functions that can make this efficient.
<?php
$sortedArray = array(
['active'] => array(
array(
'id' => 10,
'status' => 'active'
),
array(
'id' => 12,
'status' => 'active'
)
),
['closed'] => array(
array(
'id' => 11,
'status' => 'active'
)
),
['stopped'] => array(
array(
'id' => 13,
'status' => 'active'
)
),
)
$SortedArray = array();
$SortedArray['active'] = array();
$SortedArray['closed'] = array();
$SortedArray['stopped'] = array();
foreach($statuses as $Curr) {
if ($Curr['status'] == 'active') { $SortedArray['active'][] = $Curr; }
if ($Curr['status'] == 'closed') { $SortedArray['closed'][] = $Curr; }
if ($Curr['status'] == 'stopped') { $SortedArray['stopped'][] = $Curr; }
}
You can also do it with functional way though it's pretty the same like Marc said.
$sorted = array_reduce($statuses, function($carry, $status) {
$carry[$status['status']][] = $status;
return $carry;
}, []);
I've got a multidimensional PHP array in that form:
array(
(int) 0 => array(
'Category' => array(
'id' => '01',
'title' => 'SomeCategory'
)
),
(int) 1 => array(
'Category' => array(
'id' => '02',
'title' => 'OtherCategory'
)
)
)
I want to get the title of the category with a specific id, e.g. SomeCategory when I have the id 01.
Is there a better (more performant or easier) way to do it than this one?
foreach($categories as $nestedCategory) {
foreach($nestedCategory as $category) {
if($category['id'] === $postedData['Submission.Category.0.id']) {
debug($category['title']);
}
}
}
Thanks!
If the id's are unique you could rewrite the array
foreach( $array as $n => $item ) {
$arr[$item['Category']['id']] = $item;
}
// get id 02
echo $arr['02']['Category']['title']; // output: OtherCategory
I have a basic order system that I've built in PHP. Each product can have an unlimited number of metadata entries (containing information for that product). I want to build a CSV export that will include each metadata option as a column in the export file. The export should only contain the metadata for the products in the order.
For example, if I have the following array:
order = array(
'Product1' => array(
'name' => 'TShirt',
'MetaData' => array(
'1' => array(
'key' => 'size',
'value' => 'm'
)
),
'Product2' => array(
'name' => 'Backpack',
'MetaData' => array(
'1' => array(
'key' => 'waist',
'value' => '50 cm'
),
'2' => array(
'key' => 'height',
'value' => '150 cm'
)
'3' => array(
'key' => 'chest',
'value' => '25 cm'
)
),
)
I would like the CSV to look like:
(header row): product / size / waist / height / chest
(data row): t-shirt / m / NULL / NULL / NULL
(data row): backpack / NULL / 50 cm / 150 cm / 25 cm
I know I can hardcode this, but I want it to be scalable. I know this will need to be done in 2 steps - 1) build the columns by iterating through each product and each product's meta, 2) add a row entry for each product - but I can't exactly nail down how to do it. The problem I'm running in to is knowing which column I'm inserting data into. Any help would be appreciated!
Here's what I came up with. Hope it gives you a starting point.
<?php
// Your original array (I've made a few corrections)
$order = array(
'Product1' => array(
'name' => 'TShirt',
'MetaData' => array(
'1' => array(
'key' => 'size',
'value' => 'm'
)
)
),
'Product2' => array(
'name' => 'Backpack',
'MetaData' => array(
'1' => array(
'key' => 'waist',
'value' => '50 cm'
),
'2' => array(
'key' => 'height',
'value' => '150 cm'
),
'3' => array(
'key' => 'chest',
'value' => '25 cm'
)
)
),
);
// Declare header array to receive all keys from metadata
$header = array();
// Sweep metadata portion of array to retrieve keys
foreach ($order as $product) {
foreach ($product['MetaData'] as $metadata) {
if (! in_array($metadata['key'], $header)) {
$header[] = $metadata['key'];
}
}
}
// Declare content array
$content = array();
// Sweep array again to get the appropriate key/value matches
foreach ($order as $product) {
$row = array();
$row[] = $product['name'];
// Get metadata
foreach ($header as $heading) {
$added = false; // set a flag so if there are no matches, fill with "NULL" (below)
foreach ($product['MetaData'] as $metadata) {
if ($heading == $metadata['key']) { // if the heading matches the metadata key, we got a match
$row[] = $metadata['value'];
$added = true;
break;
}
}
if (! $added) {
$row[] = 'NULL';
}
}
// Add the row to the content array
$content[] = $row;
}
// Put it all together
$csv = 'product / ' . implode(' / ', $header) . "\n";
foreach ($content as $row) {
$csv .= implode(' / ', $row) . "\n";
}
echo "<pre>$csv</pre>";
?>
Good luck!
I have the following challenging array of associative arrays in php.
array(
(int) 0 => array(
'table' => array(
'venue' => 'venue1',
'name' => 'name1'
)
),
(int) 1 => array(
'table' => array(
'venue' => 'venue1',
'name' => 'name2'
)
),
(int) 2 => array(
'table' => array(
'venue' => 'venue2',
'name' => 'name3'
)
),
(int) 3 => array(
'table' => array(
'venue' => 'venue3',
'name' => 'name4'
)
)
)
I want to extract a list of relevant names out based on the venue. I would like to implement a function ExtractNameArray($venue) such that when $venue=='venue1', the returned array will look like array('name1', 'name2')
I have been cracking my head over this. I am starting with $foreach and am stuck. How can this be done in php? Thank you very much.
first, you have to pass the array with the data to the function
second, the name of the function should start with lower character (php conventions)
try this
function extractNameArray($array, $venue) {
$results = array();
foreach($array as $key=>$value) {
if(isset($value['table']['venue'])&&$value['table']['venue']==$venue) {
isset($value['table']['name']) && $results[] = $value['table']['name'];
}
}
return $results;
}
function ExtractNameArray($venue)
{
$array = array(); // your array
$return_array = array();
foreach($array as $arr)
{
foreach($arr['table'] as $table)
{
if($table['venue'] == $venue)
{
$return_array[]['name'] = $table['name'];
}
}
}
return $return_array;
}
You must define $array with you array. Good Luck
Official PHP documentation states that filter_var_array() supports array filtering in the following format:
$data = array(
'testarray' => array('2', '23', '10', '12')
);
$args = array(
'testarray' => array('filter' => FILTER_VALIDATE_INT,
'flags' => FILTER_FORCE_ARRAY
)
);
$myinputs = filter_var_array($data, $args);
However, if the array in question is multi-dimensional and requires different filters for different parts, how would you approach defining filtering options?
As an example:
$data = array(
'testhash' => array('level1'=>'email',
'level2'=> array('23', '10', '12'))
);
Idea 1
Consider using FILTER_CALLBACK. In this way, you can write a callback function that itself uses the filter extension, thus providing a recursive ability.
function validate_array($args) {
return function ($data) use ($args) {
return filter_input_array($data, $args);
};
}
This will generate the callback functions.
$args = array(
'user' => array(
'filter' => FILTER_CALLBACK,
'options' => validate_array(array(
'age' => array('filter' => FILTER_INPUT_INT),
'email' => array('filter' => FILTER_INPUT_EMAIL)
))
)
);
This is what the config array would then look like.
Idea 2
Do not hesitate to pat me on the back for this one because I am quite proud of it.
Take an arg array that looks like this. Slashes indicate depth.
$args = array(
'user/age' => array('filter' => FILTER_INPUT_INT),
'user/email' => array('filter' => FILTER_INPUT_EMAIL),
'user/parent/age' => array('filter' => FILTER_INPUT_INT),
'foo' => array('filter' => FILTER_INPUT_INT)
);
Assume your data looks something like this.
$data = array(
'user' => array(
'age' => 15,
'email' => 'foo#gmail.com',
'parent' => array(
'age' => 38
)
),
'foo' => 5
);
Then, you can generate an array of references that map keys such as 'user/age' to $data['user']['age']. In final production, you get something like this:
function my_filter_array($data, $args) {
$ref_map = array();
foreach ($args as $key => $a) {
$parts = explode('/', $key);
$ref =& $data;
foreach ($parts as $p) $ref =& $ref[$p];
$ref_map[$key] =& $ref;
}
return filter_var_array($ref_map, $args);
}
var_dump(my_filter_array($data, $args));
Now the only question is how you deal with the mismatch between the validation record and the original data set. This I cannot answer without knowing how you need to use them.