Search code examples
phparraysmultidimensional-arraymany-to-manygrouping

Restructure 2 columns of a 2d array to invert the many-to-many relationships


I've got an array with 3 parameters: date, events and tags.

  • A date contains a unix timestamp,
  • An events is an array that contain event ids,
  • Tags is an array that contains mapped events' tags (comma-separated string if the number is not alone).

This is the array:

    Array
    (
        [date] => 1554328800
        [events] => Array
            (
                [0] => 130
                [1] => 131
                [2] => 163
            )

        [tags] => Array
            (
                [0] => 4
                [1] => "1,3,4"
                [2] => "1,3"
            )

    )

The relationship between events and tags is in the key, so the event 130 that has position 0 has tag 4.

As you can see there are some tags repeated (events 130 and 131 or 131 and 163).

How could I get an array with only repeated events like this:

    Array
    (
      [0] => Array
      (
        [date] => 1554328800
        [events] => Array
            (
                [0] => 130
                [1] => 131
            )

        [tags] => 4
      )
      [1] => Array
      (
        [date] => 1554328800
        [events] => Array
            (
                [0] => 131
                [1] => 163
            )

        [tags] => Array
           (
               [0] => 1
               [1] => 3
           )
      )
    )

Solution

  • Here is how I would do it:

    1. List the events per individual tag

      This will give several sets of events, which can be used in the next step

    2. List the tags per set of events that occurred in previous step

    3. Produce the result from step 2

    Here is the code, also runnable at 3v4l.org:

    // Sample input
    $data = [
        "date" => 1554328800,
        "events" => [130, 131, 163],
        "tags" => [4, "1,3,4", "1,3"]
    ];
    
    // 1. List the events per individual tag
    foreach($data["tags"] as $i => $val) {
        $vals = explode(",", $val);
        foreach($vals as $val) {
            $eventsByTag[$val][] = $data["events"][$i];
        }
    }
    
    // 2. List the tags per set of events
    foreach($eventsByTag as $tag => $events) {
        sort($events, SORT_NUMERIC);
        $tagsByEvents[implode(",", $events)][] = $tag;
    }
    
    // 3. produce the result
    foreach($tagsByEvents as $events => $tags) {
        $events = explode(",", $events);
        if (count($tags) == 1) $tags = $tags[0];
        $result[] = [
            "date" => $data["date"],
            "events" => $events,
            "tags" => $tags
        ];
    }
    
    print_r($result);