2

I want to dissect an array like this:

[
    "ID",
    "UUID",
    "pushNotifications.sent",
    "campaigns.boundDate",
    "campaigns.endDate",
    "campaigns.pushMessages.sentDate",
    "pushNotifications.tapped"
]

To a format like this:

{
    "ID" : 1,
    "UUID" : 1,
    "pushNotifications" : 
        {
            "sent" : 1,
            "tapped" : 1
        },
    "campaigns" :
        {
            "boundDate" : 1,
            "endDate" : 1,
            "pushMessages" :
                {
                    "endDate" : 1
                }  
        }
}

It would be great if I could just set a value on an associative array in a keypath-like manner:

//To achieve this:
$dissected['campaigns']['pushMessages']['sentDate'] = 1;

//By something like this:
$keypath = 'campaigns.pushMessages.sentDate';
$dissected{$keypath} = 1;

How to do this in PHP?

Geri Borbás
  • 15,810
  • 18
  • 109
  • 172

3 Answers3

5

You can use :

$array = [
        "ID",
        "UUID",
        "pushNotifications.sent",
        "campaigns.boundDate",
        "campaigns.endDate",
        "campaigns.pushMessages.sentDate",
        "pushNotifications.tapped"
];

// Build Data
$data = array();
foreach($array as $v) {
    setValue($data, $v, 1);
}

// Get Value
echo getValue($data, "campaigns.pushMessages.sentDate"); // output 1

Function Used

function setValue(array &$data, $path, $value) {
    $temp = &$data;
    foreach(explode(".", $path) as $key) {
        $temp = &$temp[$key];
    }
    $temp = $value;
}

function getValue($data, $path) {
    $temp = $data;
    foreach(explode(".", $path) as $ndx) {
        $temp = isset($temp[$ndx]) ? $temp[$ndx] : null;
    }
    return $temp;
}
Baba
  • 94,024
  • 28
  • 166
  • 217
  • Ah, and now I understand it. :D Walks into the array with the pointer, then set when there is no deeper. Nice solution, I could hardly find out such a simple one. – Geri Borbás May 05 '13 at 22:07
  • You might also want to consider Example 8 in [SuperVariable](https://github.com/olekukonko/SuperVariable) which has object support and [Implementing MongoDB-like Query expression object evaluation](http://stackoverflow.com/questions/14972025/implementing-mongodb-like-query-expression-object-evaluation/15145128#15145128) for other flexible way of working with array – Baba May 05 '13 at 22:49
1
function keyset(&$arr, $keypath, $value = NULL)
{
   $keys = explode('.', $keypath);
   $current = &$arr;
   while(count($keys))
   {
      $key = array_shift($keys);
      if(!isset($current[$key]) && count($keys))
      {
         $current[$key] = array();
      }
      if(count($keys))
      {
         $current = &$current[$key];
      }
   }
   $current[$key] = $value;
}

function keyget($arr, $keypath)
{
   $keys = explode('.', $keypath);
   $current = $arr;
   foreach($keys as $key)
   {
      if(!isset($current[$key]))
      {
         return NULL;
      }
      $current = $current[$key];
   }
   return $current;
}

//Testing code:
$r = array();
header('content-type: text/plain; charset-utf8');
keyset($r, 'this.is.path', 39);
echo keyget($r, 'this.is.path');
var_dump($r);

It's a little rough, I can't guarantee it functions 100%.

Edit: At first you'd be tempted to try to use variable variables, but I've tried that in the past and it doesn't work, so you have to use functions to do it. This works with some limited tests. (And I just added a minor edit to remove an unnecessary array assignment.)

EPB
  • 3,939
  • 1
  • 24
  • 26
0

In the meanwhile, I came up with (another) solution:

private function setValueForKeyPath(&$array, $value, $keyPath)
{
    $keys = explode(".", $keyPath, 2);
    $firstKey = $keys[0];
    $remainingKeys = (count($keys) == 2) ? $keys[1] : null;
    $isLeaf = ($remainingKeys == null);

    if ($isLeaf)
        $array[$firstKey] = $value;
    else
        $this->setValueForKeyPath($array[$firstKey], $value, $remainingKeys);
}

Sorry for the "long" namings, I came from the Objective-C world. :) So calling this on each keyPath, it actually gives me the output:

fields
Array
(
    [0] => ID
    [1] => UUID
    [2] => pushNotifications.sent
    [3] => campaigns.boundDate
    [4] => campaigns.endDate
    [5] => campaigns.pushMessages.endDate
    [6] => pushNotifications.tapped
)
dissectedFields
Array
(
    [ID] => 1
    [UUID] => 1
    [pushNotifications] => Array
        (
            [sent] => 1
            [tapped] => 1
        )

    [campaigns] => Array
        (
            [boundDate] => 1
            [endDate] => 1
            [pushMessages] => Array
                (
                    [endDate] => 1
                )

        )

)
Geri Borbás
  • 15,810
  • 18
  • 109
  • 172