Работа с динамическими многомерными парами ключ-значение в JSON

Имея сложную проблему, вы видите только похожие, но и более простые решения на SO.

Можно ли сгенерировать динамический ключ И динамические значения с помощью JS/JSON?

Например, предположим, что у меня есть JSON:

{
    "email": "[email protected]",
    "firstname": "Bob",
    "lastname": "Smith",
    "company": "ACME",
    "custom": {
        "services": [
            {
                "name": "svc1",
                "desc": "abcdefg",
                "selected": "true",
                "status": "None"
            },
            {
                "name": "svc2",
                "desc": "abcdefg",
                "selected": "true",
                "status": "None"
            },
            {
                "name": "svc3",
                "desc": "abcdefg",
                "selected": "false",
                "status": "None"
            },
            {
                "name": "svc4",
                "desc": "abcdefg",
                "selected": "false",
                "status": "None"
            }
        ],
        "fields": [
            {
                "name": "Products",
                "desc": "abcdef",
                "type": "multi",
                "values": [
                    {
                        "name": "Product1",
                        "desc": "abcdef"
                    },
                    {
                        "name": "Product2",
                        "desc": "abcdef"
                    }
                ],
                "services": [
                    "svc1",
                    "svc2",
                    "svc3"
                ]
            },
            {
                "name": "Wines",
                "desc": "abcdef",
                "type": "multi",
                "values": [
                    {
                        "name": "Wine 1",
                        "desc": "abcdef"
                    }
                ],
                "services": [
                    "svc4"
                ]
            },
            {
                "name": "Fruits",
                "desc": "abcdef",
                "type": "multi",
                "values": [
                    {
                        "name": "Fruit 1",
                        "desc": "abcdef"
                    },
                    {
                        "name": "Fruit 2",
                        "desc": "abcdef"
                    }
                ],
                "services": [
                    "svc4"
                ]
            }
        ]
    }
};

Мне нужно зайти в поля и для каждого поля (продукты, вина, фрукты) посмотреть, содержится ли в нем данная услуга, чтобы я мог вернуться и создать продукт, вино или фрукты для каждой услуги, которая в этом нуждается. Но я не хочу повторять названия служб более одного раза. Полученный JSON должен выглядеть примерно так:

{"svc1":["Products"], "svc2":["Products"], "svc3":["Products"], "svc4":["Fruits", "Wines"]} 

Есть надежда, что для создания динамического списка в Angular я могу просто прокручивать этот JSON, вытягивая значения для каждого продукта, фрукта, вина и т. д.

Я пробовал много вложенных циклов for и тому подобное, но всякий раз, когда я спускаюсь более чем на один слой, динамизм, кажется, останавливается. Я предполагаю, что для этого мне нужно перемещаться между объектами JS и JSON?

Прямо сейчас я пытаюсь что-то вроде этого, что не совсем работает, строковое или нет. И, возможно, я слишком много переключаюсь между объектами JSON и JS:

var outObj = [];    

var fieldItems;
        $.each(jsonObj.custom.fields, function(key, item) {
            fieldItems = item;
            fieldItems.name = item.name;
            $.each(fieldItems.services, function(key, item) {
                var serviceName = item;
//check to see if the serviceName already exists
                if (outObj.indexOf(serviceName) > -1) {
                outObj.serviceName.push(fieldItems.name);
            } else {
                outObj.push(serviceName);
            }

            });
        });

        JSON.stringify(outObj);
        console.log("outObj " + outObj);

Я получаю сообщения об ошибках «не могу прочитать свойство «push» of undefined» и тому подобное. Кажется, это должно быть возможно из одного вложенного цикла, но, может быть, мне нужно просто сделать два прохода? Любые другие предложения?


person Dave Kanter    schedule 12.08.2015    source источник
comment
if (outObj.indexOf(serviceName) > -1) должно выдать ошибку, у объектов нет метода indexOf. Поскольку это не так, я предполагаю, что это не объект, и нам нужно посмотреть, как он определен, чтобы воссоздать вашу проблему.   -  person Kevin B    schedule 13.08.2015
comment
Ой... забыл этот кусок кода, хороший звонок.   -  person Dave Kanter    schedule 13.08.2015
comment
О... ну, тогда как outObj.indexOf(serviceName) не подводит.   -  person Kevin B    schedule 13.08.2015
comment
Смотрел на это так долго, что перестал видеть... снова отредактировал.   -  person Dave Kanter    schedule 13.08.2015


Ответы (1)


Для меня это звучит как слишком сложное решение. Вы можете использовать основные методы массива javascript для фильтрации необходимой структуры. Я не уверен, что profiling_value в представленном фрагменте, поэтому я начал со структуры объекта в OP

var desiredResult = jsonObj.custom.services.reduce(function(result, service){
  result[service.name] = jsonObj.custom.fields.filter(function(field){
       return field.services.indexOf(service.name) >= 0;
   }).map(function(field){ return field.name; });
  return result;
}, {});

Это дает ожидаемый результат для указанного объекта.

reduce требуется для перебора всех сервисов и накопления результата в одном объекте. Затем для каждого сервиса поля перебираются, чтобы filter остались только те, которые содержат ссылку на этот сервис. И, наконец, список отфильтрованных полей преобразуется (map) в список строк - их имен - и вставляется в аккумулятор

person Kirill Slatin    schedule 12.08.2015
comment
Почему мне так трудно понять, что делает reduce, независимо от того, сколько раз я читал документы - person Kevin B; 13.08.2015
comment
Гах... изучил JavaScript трудным путем и много лет назад и каким-то образом никогда не разучивался старым способам делать вещи, хотя я так думаю с jQuery. Получите сокращение (удерживает меня от повторения вещей), получите фильтр... и затем, конечно, карта просто следует оттуда. Кирилл, спасибо! - person Dave Kanter; 13.08.2015
comment
@KevinB, это описание MDN помог мне. - person Frank Fajardo; 13.08.2015
comment
Также хотелось бы как-то продвигать это, потому что это отличный урок по использованию сокращения, фильтрации и сопоставления вместе, когда по отдельности они могут показаться абстрактными. - person Dave Kanter; 13.08.2015