1

I have a json with this schema

  {  
     "foo":"foo1",
     "bar":"bar1",
     "fields":[  
        {  
           "foo":"foo2",
           "bar":"bar2",
           "fields":[  
              {  
                 "foo":"foo3",
                 "bar":"bar"3,
                 "count":32
              },
              {  
                 "foo":"foo4",
                 "bar":"bar4",
                 "count":2
              }
           ],
           "count":42
        },
     ],
     "count":2
  }

I need to sort and order the items recursively so to produce a "top ten". Let's say, in this case, I have to find a "top two", that is the two items having the higher 'count' at any level. It is possibile with the php native sorting functions?

The return item is not really important. Let's say it must return the "foo" element so that, in this example case, the result could be:

foo2
foo3

And if the count where all the same (all count:1) it should return the first two encountered like

foo1
foo2
Sasha Grievus
  • 2,566
  • 5
  • 31
  • 58
  • Your problem doesn't fit into a sorting solution. Your data structure is not a linear data structures (Array, List, Queues, Stacks, etc), so clearly can't be sorted. Instead it's a tree data structure and you have to traverse it to find desired values and then sort them. Take a look at [Tree Traversal](https://en.wikipedia.org/wiki/Tree_traversal) methods. – armezit Nov 21 '17 at 20:28
  • 1
    Well, I guess you can flatten the structure into a $tempArray by traversing your tree and maintaining key as your id that you need to print out. After that you can just sort array high to low by value arsort($tempArray) and print out what you need to print out. – Tadasbub Nov 21 '17 at 20:45
  • I'm trying right now to flatten, but seems not to be simple as well :_) – Sasha Grievus Nov 21 '17 at 21:01
  • 1
    Well, that is where recursion comes in https://stackoverflow.com/questions/13443125/recursively-loop-through-multidimensional-to-create-flat-array – Tadasbub Nov 21 '17 at 21:06

2 Answers2

1

Code for resolve you issue:

    $json = file_get_contents('json.json');
$data = json_decode($json, true);

 /**
 * @param $data - Array from json_decode
 * @param $keyOfData - key of field,
 * @param $keyOfFieldSort
 * @param $keyOfEmbeddedData
 * @return mixed
 */
function mySort($data, $keyOfData, $keyOfFieldSort, $keyOfEmbeddedData)
{
    $currentElements[] = [
        'key' => $data[$keyOfFieldSort],
        'data' => $data[$keyOfData],
    ];
    if (isset($data[$keyOfEmbeddedData]))
    {
        foreach ($data[$keyOfEmbeddedData] as $item) {
            $tmp = mySort($item, $keyOfData, $keyOfFieldSort, $keyOfEmbeddedData);
            $currentElements = array_merge($currentElements, $tmp);
        }
    }
return $currentElements;
}

$result = mySort($data, 'foo', 'count', 'fields');
usort($result, function ($a, $b) {
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
});

$finalResult = array_map(function ($item){
    return $item['data'];
}, $result);

//$finalResult - sorted data without extra information
Ivan
  • 625
  • 1
  • 5
  • 18
  • Thank you very much!!! But, this works only if 'count' are sequential? If count is a random value, I mean, there are several 'count:2', several 'count:5' and so on? Sorry, for not specifying. In my case the values are random and I must select the first two value with 'count' higher (that can be the first has count:2 and the second count:1 but also the first has count:1 and the second has count:1 if 1 is the higher possible value and there are two item with count:1). – Sasha Grievus Nov 21 '17 at 21:39
  • 1
    But this is not the most optimal code, most likely you can do better – Ivan Nov 21 '17 at 21:55
  • I think there is a problem with it selecting only one item for count value. If all elements have count:1 it selects only one element, instead of the desired number that is two. I'm trying to guess how to modify the code to work in any case. :) – Sasha Grievus Nov 21 '17 at 22:01
  • Gotcha! It works if there is only one element inside of the first $data passed to the function. If foo1 has a brother it doesn't consider it. – Sasha Grievus Nov 21 '17 at 22:11
  • Adding a parent with property 'fields' containing the whole json did the trick. Awesome code! Thank you thank you thank you – Sasha Grievus Nov 21 '17 at 22:20
1
$json =<<<JSON
  {  
     "foo":"foo1",
     "bar":"bar1",
     "fields":[  
        {  
           "foo":"foo2",
           "bar":"bar2",
           "fields":[  
              {  
                 "foo":"foo3",
                 "bar":"bar3",
                 "count":3
              },
              {  
                 "foo":"foo4",
                 "bar":"bar4",
                 "count":2
              }
           ],
           "count":4
        }
     ],
     "count":1
  }
JSON;

$json = json_decode($json, true);

function flatten($array, &$flattened) {
    foreach($array as $k => $v) {
        if(isset($v['fields'])) {
            flatten($v['fields'], $flattened);
            unset($v['fields']);
        }
        $flattened[] = $v;
    }
}
flatten([$json], $flattened);
usort($flattened, function($a, $b) {
    if($a == $b) return 0;
    return $b['count'] > $a['count'] ? 1 : -1;
});

foreach($flattened as $item) {
    print $item['foo'] . "\n";
}

Output:

    foo2
    foo3
    foo4
    foo1
Progrock
  • 7,373
  • 1
  • 19
  • 25