Создание нового набора объектов данных из нескольких вложенных массивов - PullRequest
3 голосов
/ 28 мая 2019

У меня сложная структура данных с несколькими вложенными массивами.

Ниже приведена текущая структура

var contentData = {
  data: {
    content: [
      {
        type: "column",
        sections: [
          {
            sub: [
              {
                type: "heading-1",
                text: "Heading Text"
              }
            ]
          }
        ]
      },
      {
        type: "acc-item",
        sections: [
          {
            sub: [
              {
                type: "heading-1",
                text: "Heading Text"
              },
              {
                type: "ordered-item",
                text: "Item 1"
              },
              {
                type: "unordered-item",
                text: "Item 2"
              }
            ]
          }
        ]
      },
      {
        type: "acc-item",
        sections: [
          {
            sub: [
              {
                type: "heading-1",
                text: "Heading Text 2"
              }
            ]
          }
        ]
      }
    ]
  }
}

Итак, что я хотел, это

  1. Я хотел сгруппировать все ordered-item & unordered-item в новый объект, такой как {type: 'list', items:[all list items]}.

  2. Мне нужно извлечь все элементы, которые находятся внутри sub, и нажатьэто новый объект embedded, и он должен быть помещен в корневой уровень, как показано ниже,

    {type:"acc-item",embedded:[{type:"heading-1",text:"Heading Text 2"}]};

Итак, что я уже сделал,

Я могу группировать acc-item, но не ordered-item & unordered-item.

Так что мой конечный ожидаемый результат должен выглядеть следующим образом:

[{
  "type": "column",
  "embedded": [
    {
      "type": "heading-1",
      "text": "Heading Text"
    }
  ]
},
{
  "type": "acc-group",
  "items": [
    {
      "type": "acc-item",
      "embedded": [
        {
          "type": "heading-1",
          "text": "Heading Text"
        },
        {
          "type": "list",
          "items": [
            {
              "type": "ordered-item",
              "text": "Item 1"
            },
            {
              "type": "unordered-item",
              "text": "Item 2" 
            }
          ]
        }
      ]
    },
    {
      "type": "acc-item",
      "embedded": [
        {
          "type": "heading-1",
          "text": "Heading Text 2"
        }
      ]
    }
  ]
}]

Ниже приведен мой код,

var group,contentData={data:{content:[{type:"column",sections:[{sub:[{type:"heading-1",text:"Heading Text"}]}]},{type:"acc-item",sections:[{sub:[{type:"heading-1",text:"Heading Text"},{type:"ordered-item",text:"Item 1"},{type:"unordered-item",text:"Item 2"}]}]},{type:"acc-item",sections:[{sub:[{type:"heading-1",text:"Heading Text 2"}]}]}]}},types=[["list",["ordered-item","unordered-item"]],["accordion",["acc-item"]]];

var result = contentData.data.content.reduce((r, o) => {
  var type = (types.find(({ 1: values }) => values.indexOf(o.type) > -1)|| {})[0];
  if (!type) {
    r.push(o);
    group = undefined;
    return r;
  }
  if (!group || group.type !== type) {
    group = { type, items: [] };
    r.push(group);
  }
  group.items.push(o);
  return r;
}, []);

document.body.innerHTML = '' + JSON.stringify(result, null, '  ') + '
';

Ответы [ 2 ]

1 голос
/ 28 мая 2019

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

var contentData = { data: { content: [{ type: "column", sections: [{ sub: [{ type: "heading-1", text: "Heading Text" }] }] }, { type: "acc-item", sections: [{ sub: [{ type: "heading-1", text: "Heading Text" }, { type: "ordered-item", text: "Item 1" }, { type: "unordered-item", text: "Item 2" }] }] }, { type: "acc-item", sections: [{ sub: [{ type: "heading-1", text: "Heading Text 2" }] }] }] } },
    list = ["ordered-item", "unordered-item"],
    lastItems, lastEmbedded,
    result = contentData.data.content.reduce((r, { type, sections }) => {
        if (type === 'column') {
            r.push({ type, embedded: sections.reduce((q, { sub }) => q.concat(sub), []) });
            lastItems = undefined;
            lastEmbedded = undefined;
            return r;
        }
        if (!lastItems) r.push({ type: "acc-group", items: lastItems = [] });
        lastItems.push(...sections.map(({ sub }) => ({
            type,
            embedded: sub.reduce((q, o) => {
                if (list.includes(o.type)) {
                    if (!lastEmbedded) q.push({ type: 'list', items: lastEmbedded = [] });
                    lastEmbedded.push(o);
                } else {
                    q.push(o);
                    lastEmbedded = undefined;
                }
                return q;
            }, [])
        })));
        return r;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
0 голосов
/ 28 мая 2019

Методы Array.prototype и Object.prototype идеально подходят для такого рода вещей.

И вы правы, что это какая-то сложная логика.

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

Вот как я думаю, я бы сделал это.

1. Группа По типу для создания ваших групп. .

На самом деле я создаю более общее решение, о котором вы просили здесь. То есть, я не просто группирую 'acc-item', но всё.

Я быстро выполнил поиск 'group array by javascript', и он дал нам этот ответ , который предлагает использовать Array.reduce, так что давайте сделаем это.

    const groupedData = contentData.data.content.reduce((acc, cur) => {
        //Check if this indexed array already exists, if not create it. 
        const currentArray = (acc[`${cur.type}-group`] && acc[`${cur.type}-group`].items) || [];

        return {
          ...acc,
          [`${cur.type}-group`]: {
              type: `${cur.type}-group`, 
              items: [...currentArray, cur]
          }
        }
    }, {}); 

2. Теперь для каждого из этих элементов нам нужно посмотреть на их сабвуферы и сгруппировать только элементы списка.

Чтобы сделать это, мы в основном хотим найти все типы `item -> section -> sub -> и отфильтровать их в два массива. Быстрый гугл о том, как создать два массива с использованием фильтра, дает мне этот ответ.

Во-первых, нам нужно сгладить эту секцию -> подпрограмму, так что давайте просто сделаем это.

function flattenSectionsAndSubs(item) {
    return {
        type: item.type, 
        subs: item.sections.reduce((acc, cur) => ([...acc, ...cur.sub]), [])
    }; 
}

И я просто скопирую и вставлю функцию разделения в:

function partition(array, isValid) {
  return array.reduce(([pass, fail], elem) => {
    return isValid(elem) ? [[...pass, elem], fail] : [pass, [...fail, elem]];
  }, [[], []]);
}


    const listTypes = ['ordered-item', 'unordered-item']; 
    function createEmbeddedFromItem(item) {
        const [lists, nonLists] = partition(item.subs, (v) => listTypes.includes(v.type); 

      return {
         type: item.type, 
         embedded: [
             ...nonLists, 
             {
                type: "list", 
                items: lists
             }
         ]
      }
    }

Собрав все это вместе, мы получим.

const contentData = {
  data: {
    content: [{
        type: "column",
        sections: [{
          sub: [{
            type: "heading-1",
            text: "Heading Text"
          }]
        }]
      },
      {
        type: "acc-item",
        sections: [{
          sub: [{
              type: "heading-1",
              text: "Heading Text"
            },
            {
              type: "ordered-item",
              text: "Item 1"
            },
            {
              type: "unordered-item",
              text: "Item 2"
            }
          ]
        }]
      },
      {
        type: "acc-item",
        sections: [{
          sub: [{
            type: "heading-1",
            text: "Heading Text 2"
          }]
        }]
      }
    ]
  }
}


function partition(array, isValid) {
  return array.reduce(([pass, fail], elem) => {
    return isValid(elem) ? [
      [...pass, elem], fail
    ] : [pass, [...fail, elem]];
  }, [
    [],
    []
  ]);
}


function flattenSectionsAndSubs(item) {
  return {
    type: item.type,
    subs: item.sections.reduce((acc, cur) => ([...acc, ...cur.sub]), [])
  };
}

const listTypes = ['ordered-item', 'unordered-item'];

function createEmbeddedFromItem(item) {
  const [lists, nonLists] = partition(item.subs, (v) => listTypes.includes(v.type));

    return {
      type: item.type,
      embedded: [
        ...nonLists,
        {
          type: "list",
          items: lists
        }
      ]
    }
  }


  const groupedData = contentData.data.content.reduce((acc, cur) => {
    //Check if this indexed array already exists, if not create it. 
    const currentArray = (acc[`${cur.type}-group`] && acc[`${cur.type}-group`].items) || [];

    const flattenedItem = flattenSectionsAndSubs(cur);
    const embeddedItem = createEmbeddedFromItem(flattenedItem);
    return {
      ...acc,
      [`${cur.type}-group`]: {
          type: `${cur.type}-group`, 
          items: [...currentArray, embeddedItem]
      }
    }
  }, {});

  console.log(groupedData);

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

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

Дело в том, что, кажется, немного красного флажка, что вы создадите массив элементов без соответствующих структур, поэтому я сделал это таким образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...