Работа с json массивами.

Тема в разделе "Perl, Python, Ruby", создана пользователем Sorcus, 4 дек 2016.

  1. Sorcus

    Sorcus Sorcus. A New Beginning.

    Moderator
    Регистр.:
    10 июл 2011
    Сообщения:
    320
    Симпатии:
    628
    Собственно есть json массив, в котором ключи могут быть, а могут и не быть.
    Код:
    value = array['item']['value']
    array['item'] может быть пустым, либо содержать ['value']
    Если array['item'] пустой, то код выше валится с ошибкой, что нет метода для NilClass.
    Оно и понятно, но вот простого решения что-то с ходу придумать не могу, чтобы присваивать значение при наличии ключа и nil при его отсутствии.
    Вариант с if пока не рассматриваю. Ибо десятки if-ов не особо кошерно. Оборачивать в методы - ну тоже хрен знает.
    Метод ||= тоже вроде не подходит, ибо присваивает значение только если переменная == nil.
    В общем предлагайте варианты. :oops:
     
  2. grave_bird

    grave_bird Создатель

    Регистр.:
    20 авг 2015
    Сообщения:
    25
    Симпатии:
    17
    Оберни всё в трай-кетчи или используй короткие условия, которые есть почти во всех языках, вот, например, в js:
    Код:
    value = (array['item']) ? (array['item']['subitem'] || null) : null;
    Ну а вообще, условиями будет более ли менее читабельно.
    Лучше, конечно, привести конкретный код обработки массива - может быть у кого-нибудь получится переоформить код более удачно, чем сейчас.
     
    Sorcus нравится это.
  3. Dead23Angel

    Dead23Angel PHP developer

    Заблокирован
    Регистр.:
    5 дек 2013
    Сообщения:
    590
    Симпатии:
    261
    хм... а почему не сделать
    value = null;

    if (array['item']) {
    value = array['item']['value'];
    }

    это явно лучше чем if и else ибо если item пуст то условие пропускается и не тратится время на вызов else ибо value был присвоен ранее
     
  4. Sorcus

    Sorcus Sorcus. A New Beginning.

    Moderator
    Регистр.:
    10 июл 2011
    Сообщения:
    320
    Симпатии:
    628
    Т.е. ты мне предлагаешь на каждый ключ if вешать, да? :eek:
    Пока он один такой - еще ладно, но когда их десяток, то ну его в пень такое счастье.
    Да и твой вариант можно сделать чуть проще:
    Код:
    value = array['item']['value'] unless array['item'].empty?
     
  5. Dead23Angel

    Dead23Angel PHP developer

    Заблокирован
    Регистр.:
    5 дек 2013
    Сообщения:
    590
    Симпатии:
    261
    любопытно что твой вариант это тот же хрен только в другой руке, тоже самое что и из этого Перейти по ссылке по сути это те же is else только пишется короче.

    Мне вот интересно каким чудным образом бы хочешь обойти не вешая if на ключик который хочешь проверить.

    И да в руби я абсолютно не шарю
     
  6. Sorcus

    Sorcus Sorcus. A New Beginning.

    Moderator
    Регистр.:
    10 июл 2011
    Сообщения:
    320
    Симпатии:
    628
    А я и не говорил, что он чем-то отличается. Но визуально он проще для меня.
    Можно писать условия без использования if, например через массивы, если конечно уметь это делать.
    Мне интересны разные варианты и не особо важно, на чем (почти не важно...)
     
  7. Dead23Angel

    Dead23Angel PHP developer

    Заблокирован
    Регистр.:
    5 дек 2013
    Сообщения:
    590
    Симпатии:
    261
    для меня стало визуально проще писать через ? : но это в js и php, даже не знаю если ли такое в руби, видимо в руби та запись которую ты привел аналогична этой записи.

    Честно скажу условия через массивы не когда не делал, возможно оно и будет быстрее работать, но привычнее if для меня.
     
  8. Sorcus

    Sorcus Sorcus. A New Beginning.

    Moderator
    Регистр.:
    10 июл 2011
    Сообщения:
    320
    Симпатии:
    628
    Решение найдено. Т.к. стандартный метод #merge не умеет в рекурсию, это нужно делать самому.
    Пример реализации был подсмотрен в rails.
    Чтобы не проверять постоянно ключи на их наличие в хэше, используется совмещение полученного хэша с шаблоном.
    Отсутствующие ключи добавляются в хэш с пустыми значениями.
    Код:
    hash_one = {'title' => nil, 'meta' => {'seo' => nil, 'description' => nil}, 'contact' => {'phone' => nil, 'email' => nil, 'jabber' => nil}}
    hash_two = {'title' => 'Hello', 'content' => 'null', 'contact' => {'phone' => 1234567890, 'email' => 'mail@example.com'}}
    
    class Hash
            def deep_merge(hash)
                    self.merge!(hash) do |key, old, new|
                            next old.deep_merge(new) if (old.is_a?(Hash) && new.is_a?(Hash))
                            new == 'null' ? old : new
                    end
            end
    end
    p hash_one.deep_merge(hash_two)
    
     
    Последнее редактирование: 4 дек 2016
    latteo и grave_bird нравится это.
  9. latteo

    latteo Эффективное использование PHP, MySQL

    Moderator
    Регистр.:
    28 фев 2008
    Сообщения:
    1.529
    Симпатии:
    1.387
    merge хорошее решение.

    Еще вот такую функцию заценил Перейти по ссылке
    Вызываем:
    PHP:
    $value getValue($versions, ['item''value'], 'defaulValue');
    Если array['item'] или array['item']['value'] не существуют - вернёт строку 'defaulValue'. Очень удобно для пользовательского ввода и для одноуровневого массива нагляднее чем тернарный оператор "?":
     
  10. Sorcus

    Sorcus Sorcus. A New Beginning.

    Moderator
    Регистр.:
    10 июл 2011
    Сообщения:
    320
    Симпатии:
    628
    Собственно пришлось переделать немного код, ибо не обрабатывались хэши вложенные в массивы.
    Примеры:
    Hash_1:
    Код:
    hash_one = {
            'title' => nil,
            'description' => nil,
            'comment_list' => [
                    {
                            'comments' => [
                                    {
                                            'author' => nil,
                                            'date' => nil,
                                            'comment' => nil
                                    }
                            ]
                    }
            ],
            'content' => nil
    }
    
    Hash_2:
    Код:
    hash_two = {
            'title' => 'Hello World!',
            'comment_list' => [
                    {
                            'comments' => [
                                    {
                                            'author' => 'Admin',
                                            'comment' => 'Default'
                                    }
                            ]
                    }
            ]
    }
    
    Например хеш, находящийся в 'comment_list' не обрабатывался. Т.е. 'date' не добавлялся в Hash_2.
    Новый код вроде как этот тестовый кусок обрабатывает как надо. Вот он:
    Код:
    class Hash
            def deep_merge(hash)
                    puts "self[0]: #{self}\nhash[0]: #{hash}"
                    hash.merge!(self) do |key, v1, v2|
                            puts "key[0]: #{key}\nv1[0]: #{v1}\nv2[0]: #{v2}"
                            if v2.is_a?(Array)
                                    v2.each do |array|
                                            v1[0].deep_merge(array)
                                    end
                            else
                                    puts ">> Return v2"
                                    v2
                            end
                    end
            end
    
    end
    
    Соответственно вызываем код:
    Код:
    p hash_two.deep_merge(hash_one)
    А вот результат работы:
    Код:
    laptop% ruby -w merge.rb
    
    --------------------
    self[0]: {"title"=>"Hello World!", "comment_list"=>[{"comments"=>[{"author"=>"Admin", "comment"=>"Default"}]}]}
    hash[0]: {"title"=>nil, "description"=>nil, "comment_list"=>[{"comments"=>[{"author"=>nil, "date"=>nil, "comment"=>nil}]}], "content"=>nil}
    key[0]: title
    v1[0]:
    v2[0]: Hello World!
    >> Return v2
    key[0]: comment_list
    v1[0]: [{"comments"=>[{"author"=>nil, "date"=>nil, "comment"=>nil}]}]
    v2[0]: [{"comments"=>[{"author"=>"Admin", "comment"=>"Default"}]}]
    self[0]: {"comments"=>[{"author"=>nil, "date"=>nil, "comment"=>nil}]}
    hash[0]: {"comments"=>[{"author"=>"Admin", "comment"=>"Default"}]}
    key[0]: comments
    v1[0]: [{"author"=>"Admin", "comment"=>"Default"}]
    v2[0]: [{"author"=>nil, "date"=>nil, "comment"=>nil}]
    self[0]: {"author"=>"Admin", "comment"=>"Default"}
    hash[0]: {"author"=>nil, "date"=>nil, "comment"=>nil}
    key[0]: author
    v1[0]:
    v2[0]: Admin
    >> Return v2
    key[0]: comment
    v1[0]:
    v2[0]: Default
    >> Return v2
    {"title"=>"Hello World!", "description"=>nil, "comment_list"=>[{"comments"=>[{"author"=>"Admin", "date"=>nil, "comment"=>"Default"}]}], "content"=>nil}
    Нули в квадратный скобках я указывал чисто для себя. Чтобы в случае неоднократного вывода self или hash в консоль, можно было вбить индексы и по коду смотреть, где какой индекс какое значение выводил.
    Так вот, код работает. Почти...Загвоздка заключается в том, что если у нас массив, содержащий несколько хешей (например comment_list будет содержать 2 и более comments), то код опять будет падать с ошибкой на всех последующих итерациях. Так как в шаблоне только один comments. Все остальные хеши из hash_two будут мержиться с nil, а не с hash.
    Вывод: Слать далеко и на долго геморойщиков с такими JSON-массивами, в которых ключи то есть, то их нет, то используются массивы, с неограниченной вложенностью и хз каким их количество. Не стоит оно того. с*ки...