2015-09-10 4 views
0

У меня есть массив с некоторыми учениками, которые поступили на курс. Существует несколько дубликатов, и для каждого курса должен быть только один ученик.Как найти несколько дубликатов в ответе PodioItemCollection?

Пример массива:

'item_id'=> 1, 'student'=> 'Bob', 'course'=> 'Learn Piano', 'address'=>'' 
'item_id'=> 2, 'student'=> 'Sam', 'course'=> 'Learn Piano', 'address'=> 'foo street' 
'item_id'=> 3, 'student'=> 'Bob', 'course'=> 'Learn Guitar', 'address'=>'' 
'item_id'=> 4, 'student'=> 'Sam', 'course'=> 'Learn Piano', 'address'=>'' 
'item_id'=> 5, 'student'=> 'Bob', 'course'=> 'Learn Guitar', 'address'=> 'bla bla street' 
'item_id'=> 6, 'student'=> 'Sam', 'course'=> 'Learn Piano', 'address'=>'' 
'item_id'=> 7, 'student'=> 'John', 'course'=> 'Learn Guitar', 'address'=>'' 

Данные доступны через API (в противном случае вся эта вещь будет простой SQL-запрос!).

Необработанные данные выглядят следующим образом:

object(PodioItemCollection)#287 (5) { ["filtered"]=> int(45639) ["total"]=> int(45639) ["items"]=> NULL ["__items":"PodioCollection":private]=> array(10) { [0]=> object(PodioItem)#3 (5) { ["__attributes":"PodioObject":private]=> array(16) { ["item_id"]=> int(319357433) ["external_id"]=> NULL ["title"]=> string(12) "Foo Bar" ["link"]=> string(71) "https://podio.com/foo/enrolments/apps/applications/items/123" ["rights"]=> array(11) ... 

Задача состоит в том, что я не могу просто использовать array_unique или подобные, потому что мне нужно:

  1. Найти все дубликаты для студента + курс
  2. Оценить найденные дубликаты друг против друга и сохранить элемент с наибольшим количеством дополнительной информации (или слить их)
  3. Получить un -двойный «item_id» для дубликатов и использовать API для удаления элементов.

Дополнительные ограничения:

  • Я не имею никакого контроля над API.
  • Есть 44000 записей
  • Там может быть больше, чем 100 дубликатов на человек + курс
  • API-интерфейс возвращает вложенную иерархию объектов, поэтому 44000 записей используют 27GB оперативной памяти (сервер имеет 144GB играть с) и да, предел php_memory установлен на нелепый уровень !!! Это единый проект, и после этого будут приняты значения .
  • Из-за большого использования RAM вещи, такие как array_intersect будут менее популярным

Конечный результат должен быть:

'item_id'=> 1, 'student'=> 'Bob', 'course'=> 'Learn Piano', 'address'=>'' 
    'item_id'=> 2, 'student'=> 'Sam', 'course'=> 'Learn Piano', 'address'=> 'foo street' 
    'item_id'=> 5, 'student'=> 'Bob', 'course'=> 'Learn Guitar', 'address'=> 'bla bla street' 
    'item_id'=> 7, 'student'=> 'John', 'course'=> 'Learn Guitar', 'address'=>'' 

Но я также нужен доступ к «item_id на 3-х, 4,6, поэтому я могу вызвать процедуру удаления через API.

Любые идеи, как решить эту проблему с несколькими дубликатами?

+0

Использование 'array_unique' – aldrin27

+0

Извините, но это не будет работать. Мне нужно найти дубликаты с наибольшей информацией, а затем сгенерировать список элементов, которые я не хочу, чтобы их удалить. – Rucia

+0

Использовать 'if statements' – aldrin27

ответ

0

Следующая функция сделает работу для вас:

$apiData = array(
    array('item_id'=> 1, 'student'=> 'Bob', 'course'=> 'Learn Piano', 'address'=>''), 
    array('item_id'=> 2, 'student'=> 'Sam', 'course'=> 'Learn Piano', 'address'=> 'foo street'), 
    array('item_id'=> 3, 'student'=> 'Bob', 'course'=> 'Learn Guitar', 'address'=>''), 
    array('item_id'=> 4, 'student'=> 'Sam', 'course'=> 'Learn Piano', 'address'=>''), 
    array('item_id'=> 5, 'student'=> 'Bob', 'course'=> 'Learn Guitar', 'address'=> 'bla bla street'), 
    array('item_id'=> 6, 'student'=> 'Sam', 'course'=> 'Learn Piano', 'address'=>''), 
    array('item_id'=> 7, 'student'=> 'John', 'course'=> 'Learn Guitar', 'address'=>'') 
); 

function resolveDuplicate($apiData = null) 
{ 
    if(!$apiData) return false; 

    foreach ($apiData as $key => $arr) { 
    $key = $arr['student'] . ':' . $arr['course']; 
    if(!$newArr[$key]['address']){ 
     if($newArr[$key]) $itemIds[] = $newArr[$key]['item_id']; 
     $newArr[$key] = $arr; 
    } 
    else{ 
     $itemIds[] = $arr['item_id']; 
    } 
    } 

    if($newArr){ 
    foreach ($newArr as $value) { 
     $finalArr[] = $value; 
    } 
    } 

    $result['student'] = $finalArr; 
    $result['duplicates'] = $itemIds; 
    return $result; 
} 

$res = resolveDuplicate($apiData); 
echo '<pre>'; 
print_r($res); 

Выход

Array 
(
    [student] => Array 
     (
      [0] => Array 
       (
        [item_id] => 1 
        [student] => Bob 
        [course] => Learn Piano 
        [address] => 
       ) 

      [1] => Array 
       (
        [item_id] => 2 
        [student] => Sam 
        [course] => Learn Piano 
        [address] => foo street 
       ) 

      [2] => Array 
       (
        [item_id] => 5 
        [student] => Bob 
        [course] => Learn Guitar 
        [address] => bla bla street 
       ) 

      [3] => Array 
       (
        [item_id] => 7 
        [student] => John 
        [course] => Learn Guitar 
        [address] => 
       ) 

     ) 

    [duplicates] => Array 
     (
      [0] => 4 
      [1] => 3 
      [2] => 6 
     ) 

)  
+0

Удивительно! Если было несколько полей для проверки дополнительной информации, скажем, есть 6 полей для проверки, чтобы убедитесь, что у нас есть запись с наибольшим количеством информации, вы бы порекомендовали просто расширить это if (! $ newArr [$ key] ['address']) {specific line? – Rucia

+0

Да, вы можете добавить там дополнительные условия. –

1

Похоже, вы пытаетесь исправить XY problem, анализируя все пункты в то же время, когда на самом деле вам просто нужно прочитать около Podio API и как работать с PodioObjects, используя их API.

Таким образом, вместо прохождения через весь массив, траверс через объект (PodioItemCollection в данном случае), сортируя его по ключевому полю (например, student), например:

PodioItem::filter(31060, array('limit' => 20, 'offset' => 20, 'sort_by' => 'student')); 

Тогда (предполагая, что критерии сортировки), вы знаете, что если есть несколько дубликатов (курс ученика + курс), они находятся в последовательном порядке. Таким образом, если у вас одно и то же имя ученика в следующей строке, рассматривайте его как дублирующее и игнорируйте другие строки (если вы не хотите обрабатывать их как часть текущего объекта-ученика), перейдите к следующему элементу.

В этом случае вы можете избежать совместной работы со всеми записями, поэтому у вас не будет проблем с памятью.

Если проблема с памятью по-прежнему остается проблемой, используйте решение NOSQL, такое как memcached или redis, поэтому храните анализируемые элементы в памяти, освобождайте переменные от PHP, переходите к следующему и в конце проверьте соответствие (например, подсчет элементов). Также рассмотрите возможность отключения любых дополнительных расширений PHP, которые у вас есть, и может замедлить обработку (например, xdebug и т. Д.).

Смотрите также:

+0

" Если проблема с памятью по-прежнему остается проблемой [...] "- и если она по-прежнему: базовый транспортный формат, похоже, json, и там есть потоковые json-парсеры, полный документ не должен находиться в памяти и разобрался на одном большом шаге. Но предоставленный, в зависимости от сложности ответа, это может быть превышение его для одноразовой работы ;-) – VolkerK

+0

@VolkerK, этот особый очист является одноразовым, хотя я знаю, что будет БЗК которые снова должны будут рассмотреть полный список учащихся, но не должны дедуплицировать их. Я буду исследовать возможность внедрения промежуточного слоя JSON для потоковой передачи данных. Спасибо за дополнительный совет! – Rucia