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

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

  1. Sorcus

    Sorcus Sorcus. A New Beginning.

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

    Dead23Angel PHP developer

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

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

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

    Sorcus Sorcus. A New Beginning.

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

    Dead23Angel PHP developer

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

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

    Sorcus Sorcus. A New Beginning.

    Moderator
    Регистр.:
    10 июл 2011
    Сообщения:
    317
    Симпатии:
    629
    Решение найдено. Т.к. стандартный метод #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.535
    Симпатии:
    1.407
    merge хорошее решение.

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

    Sorcus Sorcus. A New Beginning.

    Moderator
    Регистр.:
    10 июл 2011
    Сообщения:
    317
    Симпатии:
    629
    Собственно пришлось переделать немного код, ибо не обрабатывались хэши вложенные в массивы.
    Примеры:
    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-массивами, в которых ключи то есть, то их нет, то используются массивы, с неограниченной вложенностью и хз каким их количество. Не стоит оно того. с*ки...