SitePoint Sponsor

User Tag List

Results 1 to 13 of 13
  1. #1
    SitePoint Addict
    Join Date
    Jul 2006
    Location
    Fionnphort, Isle of Mull, Scotland
    Posts
    349
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    Adding data to a two-dimensional array using foreach

    I have a two-dimensional array and I need to add key-value pairs to the 'inner' arrays conditionally, and then write the updated arrays back to the outer array. All the 'inner' arrays have the same structure. In the snippet below the first or outer 'foreach' is looping around calculating $dist values. It's the inner 'foreach' loop that concerns me here.

    I am trying to add the key-value pairs 'dist=$dist' and 'unit'='miles' to each $listing. They get added to $listing, but this altered $listing wasn't getting written back to $listings (plural). So I've resorted to writing the altered $listing into a new outer array, and then changing its name back to $listings afterwards. Surely there's a better way ?

    PHP Code:
    //    Calculate all the distances and add to $listing array
        
    foreach ($bus_ids as $bus_id) {
            if (
    array_key_exists($bus_id$latlongs)) {
                
    $lat $latlongs[$bus_id]['lat'];
                
    $lng $latlongs[$bus_id]['lng'];
                
    $dist distance($lat$lng$lat2$lng2true);
    //            echo $bus_id . " Distance: " . $dist;
                
    foreach ($listings as $listing) {
                    if (
    $bus_id == $listing['bus_id']) {
                        
    $listing['dist'] = $dist;
                        
    $listing['unit'] = 'miles';
                        
    $newlistings[] = $listing;
                        break;  
    // bus_id is unique
                    
    }
                }
            }
        }
        
    $listings $newlistings
    I have tried:
    PHP Code:
    foreach ($listings as $key => $listing) {
             . . .
                 
    $listings[$key] = $listing;
                 . . . 
    and also:
    PHP Code:
    foreach ($listings as &$listing) { 
    Neither seem to work, but perhaps I've not got it quite right.
    Curiously when it comes to altering the values in those same key-value pairs the assignment operator seems to work OK. This code follows almost immediately after the snippet above.
    PHP Code:
    //      $listings gets sorted by the value of $listing['dist']

    //    Convert small distances to yards
    foreach ($listings as &$listing) {
        if (isset(
    $listing['dist']) && $listing['dist'] < 0.5) {
            
    $listing['dist'] = round($listing['dist']*1760,-1);
            
    $listing['unit'] = 'yards';
        }
    //    $newlistings[] = $listing; (not needed here)
    }
    //    $listings = $newlistings; (not needed here) 
    It appears that I can't ADD to an array using the assignment operator or 'as $key => $value' construction, but it is OK for altering something that already exists. Or have I just got my syntax muddled ?
    Tim Dawson
    Isle of Mull, Scotland

  2. #2
    Keeper of the SFL StarLion's Avatar
    Join Date
    Feb 2006
    Location
    Atlanta, GA, USA
    Posts
    3,748
    Mentioned
    71 Post(s)
    Tagged
    0 Thread(s)
    You see that little & before $listing in the foreach of last code block? It's pretty significant...

    Now; what EXACTLY are you trying to do? Or, to put it in a phase my programming brain can work on it (it's only 9 AM), answer the two questions:

    What do you have BEFORE (Input)
    What do you want AFTER (Output)

  3. #3
    SitePoint Addict
    Join Date
    Jul 2006
    Location
    Fionnphort, Isle of Mull, Scotland
    Posts
    349
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by StarLion View Post
    You see that little & before $listing in the foreach of last code block? It's pretty significant...

    Now; what EXACTLY are you trying to do? Or, to put it in a phrase my programming brain can work on it (it's only 9 AM), answer the two questions:

    What do you have BEFORE (Input)
    What do you want AFTER (Output)
    Thank you for your response. I'm sorry if I did not give an adequate explanation of what I am (successfully) doing. My question was aimed at finding out if there was a better way (or why what appears to be the right way isn't working for me).

    Right at the start of this process I have an array called $listings (plural). It contains many sub-arrays (about 200) which represent records from a MySQL database. These sub-arrays all have the same structure. If I were to create them manually they'd look like this:
    PHP Code:
    listings[] = ('bus_id' => '1234''name' => 'name1', ......);
    listings[] = ('bus_id' => '1235''name' => 'name2', ......);
    and 
    so on 
    They DO NOT include the KVPs 'dist' and 'unit'. In first of the three 'foreach' loops I am finding latlong co-ordinates from another table and calculating a distance, and I'm using the second 'foreach' to INSERT NEW KVPs into each sub-array when the appropriate record is found. They then look like this:
    PHP Code:
    listings[] = ('bus_id' => '1234''name' => 'name1''dist' => $dist1'unit' => $unit......);
    listings[] = ('bus_id' => '1235''name' => 'name2''dist' => $dist2'unit' => $unit......);
    and 
    so onAt this point 'unit' defaults to miles
    Now I need the changed sub-array to be 'written' back to $listings. That's where I thought '&$value' would come in, but it didn't seem to work, so I resorted to saving the changed sub-arrays to a new 'wrapper' array ($newlistings), and then changed its name afterwards. (OK, so the way the PHP Manual describes how '&$value' works is that it allows me to work on the actual $value rather than a copy, but it achieves the same effect as what I've called 'writing back'. How you look at it is less important than what it achieves.)

    Next I sort the order of the sub-arrays within the main array by the value of 'dist' within each sub-array. That works fine, so no more on that score. EXCEPT to say that this is where it fails when I try to use' &$value' above. I get "Undefined index: dist...' which tells me the added KVPs haven't been 'written back' (as I call it) to the original $listings. So long as I write my augmented sub-arrays to another wrapper ($newlistings in my code) all is well. The presence of '&$listings' in the first code sample in my original posting made no difference, so I should probably have left it our for clarity.

    Finally in the third 'foreach' I go through the sub-arrays looking for low values of 'dist' and converting them from miles to yards, and changing the 'unit' to yards too. In this case the '&$value' seems to have the desired effect of allowing me to work on the actual listings rather than a copy, so I don't explicitly have to write them to another array.
    PHP Code:
    //    Convert small distances to yards
    foreach ($listings as &$listing) {
        if (isset(
    $listing['dist']) && $listing['dist'] < 0.5) {
            
    $listing['dist'] = round($listing['dist']*1760,-1);
            
    $listing['unit'] = 'yards';
        }
    //    $newlistings[] = $listing; (not needed here)
    }
    //    $listings = $newlistings; (not needed here) 
    As before, my question boils down very simply to: Why does '&$value' appear to work when I'm just altering existing KVPs, but NOT when I'm adding additional ones ?

    If you'd like a summary, it's like this:

    Case 1: (&$value does not work)
    Before: An array of arrays
    After: An array of arrays which (all) contain some extra KVPs

    Case2: (&$value works)
    Before: An array of arrays
    After: An array of arrays which contain the same KVPs, two of which have been altered.

    I hope this is understandable. It has become very long.
    Last edited by ramasaig; Sep 20, 2011 at 09:01. Reason: remove typo
    Tim Dawson
    Isle of Mull, Scotland

  4. #4
    Keeper of the SFL StarLion's Avatar
    Join Date
    Feb 2006
    Location
    Atlanta, GA, USA
    Posts
    3,748
    Mentioned
    71 Post(s)
    Tagged
    0 Thread(s)
    Well, in developing an answer to this, i have to ask a question.
    $dist = distance($lat, $lng, $lat2, $lng2, true);

    Where are $lat2 and $lng2 defined?

  5. #5
    SitePoint Addict
    Join Date
    Jul 2006
    Location
    Fionnphort, Isle of Mull, Scotland
    Posts
    349
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Thank you for your response. I fear we may get distracted here. The lat and long values are being pulled out of the database and fed into the 'distance' function, which returns the distance between the two points. I suggest it doesn't matter what's going on there, the point is that apparently '&$value' doesn't work if KVPs are being added to the sub-arrays. Period.

    I know the distance values are good because they run on through the script and print out on the screen.
    Tim Dawson
    Isle of Mull, Scotland

  6. #6
    Keeper of the SFL StarLion's Avatar
    Join Date
    Feb 2006
    Location
    Atlanta, GA, USA
    Posts
    3,748
    Mentioned
    71 Post(s)
    Tagged
    0 Thread(s)
    Well my point was that $lat2 and $lon2 dont get set on each iteration of the loop, so you may be getting eroneous distances, unless all distances are being measured from a single point.

    anyway, here's how i'd do it.
    PHP Code:
    foreach($listings AS &$listing) {
      if (
    array_key_exists($listing['bus_id'], $latlongs)) {
        
    $listing['dist'] = distance($latlongs[$listing['bus_id']]['lat'], $latlongs[$listing['bus_id']]['lng'], $lat2$lng2true); 
        
    $listing['unit'] = 'miles';
      }


  7. #7
    SitePoint Addict
    Join Date
    Jul 2006
    Location
    Fionnphort, Isle of Mull, Scotland
    Posts
    349
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Thank you. All the distances are indeed calculated from a single point (the user's location, which can be changed elsewhere). Your suggestion shortens the code quite a bit (at the price of making it harder to follow, perhaps). However, it still doesn't work unless I reintroduce my $newlistings.
    PHP Code:
        foreach($listings AS &$listing) {
          if (
    array_key_exists($listing['bus_id'], $latlongs)) {
            
    $listing['dist'] = distance($latlongs[$listing['bus_id']]['lat'], $latlongs[$listing['bus_id']]['lng'], $lat2$lng2true);
            
    $listing['unit'] = 'miles';
                
    $newlistings[] = $listing;
          }
        }
        
    $listings $newlistings
    Without it I still get the 'Undefined Index: dist' error.

    You can see it in rudimentary form at www.mobile.holidaymullandiona.com
    Click on one of the location buttons (NOT geo-location, it's not working yet) and the on 'Accommodation'/'Eating'/'Attractions'/
    You should be presented with a list in ascending order of distance from your chosen location. There's no built-in back button, but if you do go back you can repeat from a different dimension.
    Tim Dawson
    Isle of Mull, Scotland

  8. #8
    Keeper of the SFL StarLion's Avatar
    Join Date
    Feb 2006
    Location
    Atlanta, GA, USA
    Posts
    3,748
    Mentioned
    71 Post(s)
    Tagged
    0 Thread(s)
    I've done this in my test enviroment:
    PHP Code:
    <?php
    $vals 
    = array("moo","cow");
    $arr = array(array(),array());
    foreach(
    $arr AS $key => &$myarr) {
      if(
    array_key_exists($key,$vals)) {
        
    $myarr['new'] = $vals[$key];
      }
    }
    print_r($arr);
    ?>
    works absolutely fine. So the system should work... are you using an older version of PHP? (not that that should change anything, really)

  9. #9
    SitePoint Addict
    Join Date
    Jul 2006
    Location
    Fionnphort, Isle of Mull, Scotland
    Posts
    349
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Thank you.

    Your test version certainly worked, and so I modified it to bring it closer to my script. As you can see, instead of running a calculation within the IF clause (as in my original), I've added on two KVPs from the $latlong array.
    PHP Code:
    <?php
    $listings 
    = array (
        
    => array("bus_id" => "1234""name" => "Jim"),
        
    => array("bus_id" => "1235""name" => "Fred")
        );

    $latlongs = array (
        
    "1234" => array("lat" => 56.31582"lng" => 6.23551),
        
    "1235" => array("lat" => 55.58868"lng" => 5.01604)
        );

    foreach(
    $listings AS &$listing) {
        if(
    array_key_exists($listing['bus_id'],$latlongs)) {
            
    $listing['lat'] = $latlongs[$listing['bus_id']]['lat'];
            
    $listing['lng'] = $latlongs[$listing['bus_id']]['lng'];
            
    $listing['unit'] = 'miles';
        }
    }

    print_r($listings);
    //Prints out:
    /* Array (
        [0] => Array ( [bus_id] => 1234 [name] => Jim [lat] => 56.31582 [lng] => 6.23551 [unit] => miles )
        [1] => Array ( [bus_id] => 1235 [name] => Fred [lat] => 55.58868 [lng] => 5.01604 [unit] => miles )
        )
    */
    ?>
    It still works, which leaves me wondering why my original version doesn't (without introducing the $newlisting array). Perhaps there's some important aspect I've overlooked. I shall continue to look at it, but I'm not going to go grey over a couple of lines of code.
    Thank you for your help.
    Tim Dawson
    Isle of Mull, Scotland

  10. #10
    SitePoint Member
    Join Date
    Sep 2011
    Location
    Melbourne, 3000, Australia
    Posts
    3
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    As far as I know, foreach will only loop though the elements of the selected array

    if you want to display all the dimentions of an array use the function

    print_r($array);

    if you only want to print out certain parts of an array, you could use this function.

    function PrintArray($array)
    {

    if(is_array($array))
    {

    foreach($array as $key=>$value)
    {

    if(is_array($value)
    {

    // the value of the current array is also a array, so call this function again to process that array

    PrintArray($value)

    }
    else
    {

    // This part of the array is just a key/value pair

    echo "$key: $value<br>";

    }


    }

    }

    }
    that will do the same as print_r(); however you can then limit what is printed by placing some conditions where i have the line

    echo "$key: $value<br>";

    so you could change that to

    if($key == $name)
    {
    echo "$key: $value<br>";
    }

    so now it will only print the array element when key == name

    Hope you get help.
    Jeffrey

    ______________________
    Renewgraphicdesign.com.au

  11. #11
    SitePoint Addict
    Join Date
    Jul 2006
    Location
    Fionnphort, Isle of Mull, Scotland
    Posts
    349
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Hello Jeffrey. The issue isn't about the printing, it's about using a 'foreach' loop to tack additional KVPs onto a two-dimensional array. My last posting probably doesn't make much sense to someone picking up the thread now.
    Tim Dawson
    Isle of Mull, Scotland

  12. #12
    SitePoint Member
    Join Date
    Sep 2011
    Location
    Melbourne, 3000, Australia
    Posts
    3
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Thumbs up

    Hi,

    Well, I think this may work for you.

    PHP Code:
    function merge_db_lists ($list1$list2) {
      
    $final_array = array();
      
    $final_array go_through_list($list1$final_array);
      
    $final_array go_through_list($list2$final_array);
      return 
    $final_array;
    }   

    function 
    go_through_list($list,$output){
      foreach (
    array_keys($list) as $key){
        if (
    array_key_exists($key$output)){
          foreach (
    $list[$key] as $item ){
            
    $output[$key][] = $item;
          }  
          
    arsort($output[$key]);
        }
        else{
          
    $output[$key] = $list[$key];
        }  
      }
      return 
    $output;

    Thank you.

  13. #13
    SitePoint Addict
    Join Date
    Jul 2006
    Location
    Fionnphort, Isle of Mull, Scotland
    Posts
    349
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Thanks, Jeffery. It seems almost more complicated than what I was doing before. Since my original code (writing the augmented arrays into the $newlisting array) works perfectly well I'm going to leave it at that for now. I'm sure i'll come back to it when I've nothing else to do.

    Thank you both for your interest and your time.
    Tim Dawson
    Isle of Mull, Scotland


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •