I have been trying unsuccessfully to access and manipulate certain name value pairs within an array when in a foreach loop.
My data is in an array like this:
[0] => Array
(
[subject] => math
[price] => 5
[year] => 2006
)
[1] => Array
(
[subject] => reading
[price] => 7
[year] => 2007
[author] => Smith
[publisher] => Press
)
[2] => Array
(
[subject] => reading
[price] => 9
[year] => 2008
[author] => Jones
[copyright] => 1999
)
My code is:
$count = count($array);
for ($i = 0; $i < $count; $i++) {
foreach($array[$i] as $name => $value) {
if(preg_match('(subject|price|year)', $name) != 1) {
#$desc .= '-'.$name.'-'.$value;
} else {
$desc = '';
}
echo $i.' : '.$desc.'<br />';
}
}
My desired output from the code above would be:
0 : subject-math / price-5 / year-2006
1 : subject-reading / price-7 / year-2007 / author-Smith-publisher-Press
2 : subject-reading / price-9 / year-2008 / author-Jones-copyright-1999
The main issue I am facing is that I don't know how to combine & echo out all of the name value pairs that don't match the preg_match condition. Essentially subject, price and year and common to every record but any of the others I want to be able to access all merged together as one item.
Thanks in advance for any help!
This code will do what you want. It loops through your array, pushing all the key-value pairs into an array and then echo'ing an implode (with /) of the array. Values for subject, price and year have their own entries, while all other values are pushed into an array which is then also imploded using - to give your desired output. Rather than using preg_match to match keys, a simple in_array is used instead:
foreach ($data as $k => $d) {
$out = array();
foreach ($d as $key => $value) {
if (in_array($key, ['subject', 'price', 'year'])) {
$out[] = "$key-$value";
}
else {
$out['others'][] = "$key-$value";
}
}
if (isset($out['others'])) $out[] = implode('-', $out['others']);
unset($out['others']);
echo "$k : " . implode(' / ', $out) . "\n";
}
Output:
0 : subject-math / price-5 / year-2006
1 : subject-reading / price-7 / year-2007 / author-Smith-publisher-Press
2 : subject-reading / price-9 / year-2008 / author-Jones-copyright-1999
Demo on 3v4l.org
I guess, it'll be the most simple implementation:
foreach ($array as $index => $item)
{
$result = array_filter
([
'subject-' . array_shift($item),
'price-' . array_shift($item),
'subject-' . array_shift($item),
implode('-', $item)
]);
$result = implode(' / ', $result);
echo "$index: $result\n";
}
Related
I have a multidimensional array. The arrays will have different lengths and seldom will they have the same length. My problem here is how can I make it so that the arrays will all share the length of the array with the biggest size?
My Array:
Array
(
[1] => Array
(
[Session 2] => Beer
[Food] => Chicken
[Drink] => Beer
)
[2] => Array
(
[Session 2] => Tea
[Food] => Aaaa
[Drink] => Ddd
[Cake] => Weee
[Brownies] => Rrrr
)
)
Expected output:
Array
(
[1] => Array
(
[Session 2] => Beer
[Food] => Chicken
[Drink] => Beer
[Cake] => ''
[Brownies] => ''
)
[2] => Array
(
[Session 2] => Tea
[Food] => Aaaa
[Drink] => Ddd
[Cake] => Weee
[Brownies] => Rrrr
)
)
The array size is not limited to only two arrays. Is this even possible and if so how?
I only want to copy the array keys and not the values its main purpose here is for presenting the content of the array in a table.
Here's one option, where you build an array of all possible array keys, then loop over your original array and set empty strings to the keys that don't exist yet:
// find all possible keys
$keys = [];
foreach ($array as $entry) {
$keys = array_merge($keys, array_keys($entry));
}
// pad missing keys with an empty string
foreach ($array as &$entry) {
foreach ($keys as $key) {
if (!isset($entry[$key])) {
$entry[$key] = '';
}
}
}
If the main purpose is to show the data in a table, then you do not need to fill in the missing keys. You can use the isset() or empty() functions to determine whether an array has a given key. So, your table code could look like the following:
<?php
foreach ($rows as $row) {
echo "<tr>";
echo "<td>" . (isset($row["Session 2"]) ? $row["Session 2"] : "") . "</td>"; //Old school
echo "<td>" . ($row["Food"] ?? "") . "</td>"; //PHP 7+
//remaining rows
echo "</tr>";
}
Let's say the array you're talking about is inside a variable $array,
Do this to find the maximum length;
$max = 0;
foreach($array as $index => $value){
if($index == sizeof($array) - 1){
break;
}
if($index && $max > sizeof($array[$index+1])){
$max = $max;
}
if(!$index && sizeof($value) > sizeof($array[$index+1])){
$max = sizeof($value);
}else {
$max = sizeof($array[$index+1]);
}
}
I've got a multidimensional array.
I need a way to tally up the total value when both the 1st and second strings in the array occur multiple times.
So for instance :
Gold Metallic = 22
Black Toscano = 26
etc...
Any ideas?
[0] => Array
(
[0] => Array
(
[0] => Black
[1] => Toscano
[2] => 14
)
[1] => Array
(
[0] => Gold
[1] => Metallic
[2] => 10
)
)
[1] => Array
(
[0] => Array
(
[0] => Gold
[1] => Metallic
[2] => 12
)
[1] => Array
(
[0] => Black
[1] => Toscano
[2] => 12
)
)
This just solves the problem for your data structure so you have to make sure that, in practice, every two items you will get a number. Hope you can learn something from this :)
$products = array(
array(
array("Black", "Toscano", 14),
array("Gold", "Metallic", 10)
),
array(
array("Black", "Toscano", 12),
array("Gold", "Metallic", 12)
),
);
$accumulated = array();
$key = "";
$callback = function($item, $index) use(&$key, &$accumulated) {
if($index != 2) {
$key .= $item;
} else {
if(!array_key_exists($key, $accumulated)) {
$accumulated[$key] = 0;
}
$accumulated[$key] += $item;
$key = "";
}
};
array_walk_recursive($products, $callback);
var_dump($accumulated);
Should be a simple case of looping over the data and storing an array of sums. This is one possibility using a hash with keys as the pairs concatenated with a separator sentinel value.
$separator = "||"; //take care to choose something that doesn't pop up in your data here
//$data = example data;
$pairs = array();
foreach ($data as $val) {
foreach ($val as $pair) {
$str = $pair[0] . $separator . $pair[1];
if (array_key_exists($str, $pairs))
$pairs[$str] += $pair[2];
else
$pairs[$str] = $pair[2];
}
}
print_r($pairs);
output:
["Black||Toscano"] => 26,
["Gold||Metallic"] => 22
The data can be easily retrieved at this point
foreach ($pairs as $str => $sum) {
$str = explode($separator, $str);
echo $str[0] . ", " . $str[1] . ": " . $sum;
}
let's say we have the following array:
Array ( [0] => 123456 [1] => Rothmans Blue [2] => 40 [3] => RB44 [4] => 1 )
I want to reprint this array, with the [4]th key having an additional +1, like so:
Array ( [0] => 123456 [1] => Rothmans Blue [2] => 40 [3] => RB44 [4] => 2 )
Then again:
Array ( [0] => 123456 [1] => Rothmans Blue [2] => 40 [3] => RB44 [4] => 3 )
EDIT: The solutions given below work, however, my code does not increment the 4th key:
$filew = 'databases/stocktakemain.csv';
$getfilecont = file_get_contents($filew);
$writes = explode(",", $getfilecont);
++$writes[4];
Is there an issue with this code? Does this not apply when creating arrays through explode?
you can use the ++ to incremente in 1 your 4th array value
<?php
$array[0] = 123456;
$array[1] = 'Rothmans Blue';
$array[2] = 40;
$array[3] = 'RB44';
$array[4] = 1;
echo ++$array[4] . "<br>\n";
echo ++$array[4] . "<br>\n";
echo ++$array[4] . "<br>\n";
?>
At the end of your code, you can put
Array[4] == Array[4] + 1;
This will print out 10 arrays with that last key being an incrementing number, change the $max to make it bigger/smaller.
$max = 10;
for( $i=1; $i<=$max; $i++ ) {
print_r( array(
123456,
'Rothmans Blue',
40,
'RB44',
$i
));
}
In this case, you don't actually need to declare the key values as PHP considers the index of a value in an array to be its key.
You might try something like this if you want to print the values, then increment:
$myArray = array(123456, "Rothmans Blue", 40, "RB44", 1);
for ($i= 0; $i < 3; $i++)
{
foreach ($myArray as $key => $value)
{
print $key . " : " . $value;
if ($key == 4)
{
print "\n";
$myArray[$key] += 1; // Make sure to modify the original array, not the one you passed in as it is passed by reference.
}
}
}
If you want to increment and then print, move the print statement to the bottom of the foreach loop.
You can use end($array) function and get the last element and then add.
I need to modify one PHP array and I really have hard time with it :(
Array ( [0] => product_category-big-savings [1] => product_category-for-men [2] => product_category-jeans [3] => brand-3-1-phillip-lim [4] => size-10 [5] => )
That is the array output, I want to convert it to be like this:
Array ( [product_category] => big-savings, for-men, jeans [brand] => 3-1-phillip-lim [size] => 10 )
I really can't get the hang of it. Here is what I have tried:
foreach($arr as $k => $v) {
if ($v != '') {
$arr = explode('-', $v, 2);
$terms[] = $arr[1];
$taxes[] = $arr[0];
}
}
The first array consists of keys/values. The values are the terms I am targetting: for example:
product_category-big-savings is my target and I use this to split the category from its value:
$arr = explode('-', $v, 2);
Which gives me:
product_category ->
big-savings ->
And so on. How to group all product categories after that in array with product_category as key? and so on.
But I get an even more complicated form after that. I assume there is a simple formula that rolls this array over and convert it in the desired format. any help appreciated pleasee...
list($key, $value) = explode('-', $v, 2);
if (isset($result[$key])) {
$result[$key] .= ', ' . $value;
} else {
$result[$key] = $value;
}
You should do this instead:
foreach($arr as $k => $v) {
if ($v != '') {
$arr = explode('-', $v, 2);
if (!isset($result[$arr[0]])){
$result[$arr[0]] = $result[$arr[1]];
}
else {
$result[$arr[0]] .= ', '.$result[$arr[1]];
}
}
}
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
In my data mining project, I'm given a complicated, huge multidemensional array of arrays that contains all the info I require, except that I have to perform a "fix" on it before I can process it. I've written some code that takes care of the issue, but it's taking way too long for the huge amount of data I have to "fix," and I'm hoping someone can help me find a more efficient solution.
Essentially, the type of array I'm working with is first indexed by an integer, as any run-of-the-mill array would, i.e. $x[0], $x[1], $x[2], except that each element is an associative array that contains key-pair values that I need (such as $x[0]['item'], $x[0]['price']), however one key is stored a bit deeper, the ID.
An ID number exists in the array as $x[0]['#attributes']['id'], and I would like to simplify the structure by duplicating this info along with the other key pairs, like $x[0]['id'].
The data set I'm working with is large, but here is a simplified example of my situation:
$attrib1 = array('id'=>'101');
$item1 = array('#attributes'=>$attrib1, 'item'=>'milk', 'price'=>'3.50');
$attrib2 = array('id'=>'102');
$item2 = array('#attributes'=>$attrib2, 'item'=>'butter', 'price'=>'2.45');
$attrib3 = array('id'=>'103');
$item3 = array('#attributes'=>$attrib3, 'item'=>'bread', 'price'=>'1.19');
$items = array($item1, $item2, $item3);
echo "Starting data - items using itemid as attribute:\n";
print_r($items);
# set item numbers by key instead of attribute
$i=0;
while(isset($items[$i]['#attributes']['id'])) {
$items[$i]['itemid'] = $items[$i]['#attributes']['id'];
#unset($items[$i]['#attributes']);
$i++;
} # while
echo "\nDesired result - items using itemid as key:\n";
print_r($items);
Here is the output from that above example:
Starting data - items using itemid as attribute:
Array
(
[0] => Array
(
[#attributes] => Array
(
[id] => 101
)
[item] => milk
[price] => 3.50
)
[1] => Array
(
[#attributes] => Array
(
[id] => 102
)
[item] => butter
[price] => 2.45
)
[2] => Array
(
[#attributes] => Array
(
[id] => 103
)
[item] => bread
[price] => 1.19
)
)
Desired result - items using itemid as key:
Array
(
[0] => Array
(
[#attributes] => Array
(
[id] => 101
)
[item] => milk
[price] => 3.50
[itemid] => 101
)
[1] => Array
(
[#attributes] => Array
(
[id] => 102
)
[item] => butter
[price] => 2.45
[itemid] => 102
)
[2] => Array
(
[#attributes] => Array
(
[id] => 103
)
[item] => bread
[price] => 1.19
[itemid] => 103
)
)
Note the added [itemid] key-value pair in the desired result. Is there a faster / more elegant way of accomplishing this? I've looked at some of PHP's fancy array functions, but I can't wrap my head around this more complicated situation to make use of them. Any ideas?
Memory Efficiency
PHP DOC Comments : Memory footprint of splFixedArray is about 37% of a regular "array" of the same size.
splFixedArray also implements Iterator which means it encapsulate the list and expose visibility to one element at a time making them far more efficient.
The foreach loop makes a copy of any array passed to it. If you are processing a large amount of data, using it directly with our array can be a performance issue
Also see How big are PHP arrays (and values) really? (Hint: BIG!)
You can try
$it = SplFixedArray::fromArray($items);
foreach ( $it as $value ) {
// Play with big array
}
Speed
Here is a simple benchmark
set_time_limit(0);
echo "<pre>";
$total = 10000;
$item = array("milk","butter","bread");
$items = array();
// Generating Random Data
for($i = 0; $i < $total; $i ++) {
$att = array('id' => $i);
$items[] = array('#attributes' => $att,'item' => $item[$i % 3],'price' => mt_rand(100, 5000) / 100);
}
// Pure array no copy
function m1($array) {
foreach ( $array as $k => $v ) {
isset($v['#attributes']) and $array[$k]['id'] = $v['#attributes']['id'];
unset($array[$k]['#attributes']);
}
return $array;
}
// Array clean copy
function m2($array) {
$items = array();
foreach ( $array as $k => $v ) {
isset($v['#attributes']) and $items[$k]['id'] = $v['#attributes']['id'];
$items[$k]['item'] = $v['item'];
$items[$k]['price'] = $v['price'];
}
return $items;
}
// Array Iterator
function m3($array) {
$it = new ArrayIterator($array);
$items = array();
foreach ( $it as $k => $v ) {
isset($v['#attributes']) and $items[$k]['id'] = $v['#attributes']['id'];
$items[$k]['item'] = $v['item'];
$items[$k]['price'] = $v['price'];
}
return $items;
}
// SplFixedArray Array
function m4($array) {
$it = SplFixedArray::fromArray($array);
$items = array();
foreach ( $it as $k => $v ) {
isset($v['#attributes']) and $items[$k]['id'] = $v['#attributes']['id'];
$items[$k]['item'] = $v['item'];
$items[$k]['price'] = $v['price'];
}
return $items;
}
// Array Map
function m5($array) {
$items = array_map(function ($v) {
isset($v['#attributes']) and $v['id'] = $v['#attributes']['id'];
unset($v['#attributes']);
return $v;
}, $array);
return $items;
}
// Array Walk
function m6($array) {
array_walk($array, function (&$v, $k) {
isset($v['#attributes']) and $v['id'] = $v['#attributes']['id'];
unset($v['#attributes']);
return $v;
});
return $array;
}
$result = array('m1' => 0,'m2' => 0,'m3' => 0,'m4' => 0,'m5' => 0,'m6' => 0);
for($i = 0; $i < 1; ++ $i) {
foreach ( array_keys($result) as $key ) {
$alpha = microtime(true);
$key($items);
$result[$key] += microtime(true) - $alpha;
}
}
echo '<pre>';
echo "Single Run\n";
print_r($result);
echo '</pre>';
$result = array('m1' => 0,'m2' => 0,'m3' => 0,'m4' => 0,'m5' => 0,'m6' => 0);
for($i = 0; $i < 2; ++ $i) {
foreach ( array_keys($result) as $key ) {
$alpha = microtime(true);
$key($items);
$result[$key] += microtime(true) - $alpha;
}
}
echo '<pre>';
echo "Dual Run\n";
print_r($result);
echo '</pre>';
It has a very Interesting results
PHP 5.3.10
Single Run
Array
(
[m1] => 0.029280185699463 <--------------- fastest
[m2] => 0.038463115692139
[m3] => 0.049274921417236
[m4] => 0.03856086730957
[m5] => 0.032699823379517
[m6] => 0.032186985015869
)
Dual Run
Array
(
[m1] => 0.068470001220703
[m2] => 0.077174663543701
[m3] => 0.085768938064575
[m4] => 0.07695198059082
[m5] => 0.073209047317505
[m6] => 0.065080165863037 <--------------- Fastest after in 2 loops
)
PHP 5.4.1
Single Run
Array
(
[m1] => 0.029529094696045
[m2] => 0.035377979278564
[m3] => 0.03830099105835
[m4] => 0.034613132476807
[m5] => 0.031363010406494
[m6] => 0.028403043746948 <---------- fastest
)
Dual Run
Array
(
[m1] => 0.072367191314697
[m2] => 0.071731090545654
[m3] => 0.078131914138794
[m4] => 0.075049877166748
[m5] => 0.065959930419922
[m6] => 0.060923099517822 <---------- Fastest
)
That looks like it's coming from XML, so i would add that it's possible for #attributes to have more than just ID in it.. but assuming that won't happen you could try using a foreach instead, though I'm not sure about speed gains.
There may be an impact because you are modifying the same array you are looping (I can't find evidence for this though, so experiment required)
$cleanedArray = array();
foreach($bigArray as $subArray)
{
if(isset($subArray['#attributes']))
{
$subArray['itemid'] = $subArray['#attributes']['id'];
unset($subArray['#attributes']); //Optional
$cleanedArray[] = $subArray;
}
}
Apologies if that ends up slower
Edit: Missing index added
This isn't an answer so much as it is a comparison of the approaches provided:
I used this script to average out the times the algorithms took:
<?php
//base data
$attrib1 = array('id'=>'101');
$item1 = array('#attributes'=>$attrib1, 'item'=>'milk', 'price'=>'3.50');
$attrib2 = array('id'=>'102');
$item2 = array('#attributes'=>$attrib2, 'item'=>'butter', 'price'=>'2.45');
$attrib3 = array('id'=>'103');
$item3 = array('#attributes'=>$attrib3, 'item'=>'bread', 'price'=>'1.19');
$results = array('test1'=>array(),'test2'=>array(),'test3'=>array());
//set trials
$trials=1000;
//test 1
for($count=0;$count<$trials;$count++){
unset($items);
$items = array($item1, $item2, $item3);
$timer1=microtime();
$i=0;
while(isset($items[$i]['#attributes']['id'])) {
$items[$i]['itemid'] = $items[$i]['#attributes']['id'];
$i++;
}
$timer1=microtime()-$timer1;
$results['test1'][$count]=$timer1;
}
//test 2
for($count=0;$count<$trials;$count++){
unset($items);
unset($cleanedArray);
$items = array($item1, $item2, $item3);
$cleanedArray = array();
$timer2=microtime();
foreach($items as $subArray)
{
if(isset($subArray['#attributes']))
{
unset($subArray['#attributes']);
$cleanedArray[] = $subArray;
}
}
$timer2=microtime()-$timer2;
$results['test2'][$count]=$timer2;
}
//test 3
for($count=0;$count<$trials;$count++){
unset($items);
unset($it);
$items = array($item1, $item2, $item3);
$it = SplFixedArray::fromArray($items);
$timer3=microtime();
foreach($it as $subArray)
{
if(isset($subArray['#attributes']))
{
unset($subArray['#attributes']);
$cleanedArray[] = $subArray;
}
}
$timer3=microtime()-$timer3;
$results['test3'][$count]=$timer3;
}
//results
$factor=pow(10,-6);
echo "Test 1 averaged " . round(array_sum($results['test1']) / count($results['test1'])/$factor,1) . " µs, with range: " . round((max($results['test1'])-min($results['test1']))/$factor,1) . " µs - (min: " . (min($results['test1'])/$factor) . ", max: " . (max($results['test1'])/$factor) . ")<br/>";
echo "Test 2 averaged " . round(array_sum($results['test2']) / count($results['test2'])/$factor,1) . " µs, with range: " . round((max($results['test2'])-min($results['test2']))/$factor,1) . " µs - (min: " . (min($results['test2'])/$factor) . ", max: " . (max($results['test2'])/$factor) . ")<br/>";
echo "Test 3 averaged " . round(array_sum($results['test3']) / count($results['test3'])/$factor,1) . " µs, with range: " . round((max($results['test3'])-min($results['test3']))/$factor,1) . " µs - (min: " . (min($results['test3'])/$factor) . ", max: " . (max($results['test3'])/$factor) . ")<br/>";
echo "<pre>";
var_dump($results);
echo "</pre>";
The results here are extremely variable at low numbers of trials, but should become more skewed if the base array is larger and larger numbers of trials are run.