создать таблицу, которая отображает иерархию объектов из объекта JSON - PullRequest
1 голос
/ 15 января 2020

У меня есть следующий объект:

let data = {
  A: {
    B1: {
      C1: {
        D1: "foo"
      },
      C2: {
        D2: "bar",
        D3: "baz"
      }
    },
    B2: {
      C3: {
        D4: "qux"
      },
      C4: {
        D5: "quux"
      }
    }
  }
};

Я хотел бы создать функцию, которая создает таблицу, отображающую вложенную структуру в виде, похожем на диаграмму Санки:
table

1 Ответ

1 голос
/ 15 января 2020

Я разбил свое решение на некоторый псевдокод:

get_table(data):
  # Map resulting rows with "tr" elements
  return recurse_dataset.map(row => "<tr>row</tr>")

# Return list of rows (only "td" elements)
recurse_dataset(data):
  if data is root:
    return data
  else:
    # Get all child rows and add to a result
    result = []
    for key, value in data:
      roots = number_roots(value)
      # Recurse further into object to get root rows
      for index, row in recurse_dataset(value):
        # If we are the first result and we have children,
        #   add our key cell with overlap
        if index == 0 and roots > 0:
          row = "<td rowspan=roots>key</td>" + row
        result += row
    return result

number_roots(data):
  if data is root:
    return 1
  else:
    count = 0
    for value in data:
      count += number_roots(value)
    return count

Или, во фрагменте:

const getTable = function(data) {
  return getRows(data).map(row => `<tr>${row}</tr>`).join("");
};

const getRows = function(data) {
  if(typeof data !== "object") {
    return [
      `<td>${data}</td>`
    ];
  }
  else {
    let result = [];
    for(let [key, value] of Object.entries(data)) {
      let children = getChildrenCount(value);
      let rows = getRows(value);
      rows.forEach((row, index) => {
        if(index == 0 && children) {
          row = `<td rowspan="${children}">${key}</td>
${row}`;
        }
        result.push(row);
      });
    };
    return result;
  }
};

const getChildrenCount = function(data) {
  if(typeof data !== "object") {
    return 1;
  }
  else {
    let count = 0;
    for(let [key, value] of Object.entries(data)) {
      count += getChildrenCount(value);
    };
    return count;
  }
};

let data = {
  A: {
    B1: {
      C1: {
        D1: "foo"
      },
      C2: {
        D2: "bar",
        D3: "baz"
      }
    },
    B2: {
      C3: {
        D4: "qux"
      },
      C4: {
        D5: "quux"
      }
    }
  }
};

$("tbody").append(getTable(data));
table td,
table th {
    min-width: 2em;
    min-height: 2em;
    border: 1px solid #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<table>
  <tbody></tbody>
</table>

И если вам нужна строка «итоговая», которая показывает, сколько элементов в каждой группе:

const getTable = function(data) {
  return getRows(data).map(row => `<tr>${row}</tr>`).join("");
};

const getRows = function(data) {
  if(typeof data !== "object") {
    return [
      `<td>${data}</td>`
    ];
  } 
  else {
    let result = [];
    for(let [key, value] of Object.entries(data)) {
      let children = getChildrenCount(value);
      let depth = getDepth(value);
      let rows = getRows(value);
      rows.forEach((row, index) => {
        if(index == 0 && children) {
        	result.push(`<td rowspan="${children + getExtraSpan(value)}">${key}</td>${row}`);
        }
        else {
        	result.push(row);
        }
      });
      if(depth) {
        result.push(`<td ${depth ? `colspan="${depth}"` : ""}>Count</td><td>${children}</td>`);
      }
    };
    return result;
  }
};

const getDepth = function(data) {
  if(typeof data !== "object") {
    return 0;
  }
  else {
    let count = 0;
    for(let [key, value] of Object.entries(data)) {
      count = Math.max(count, getDepth(value));
    };
    return count + 1;
  }
};

const getChildrenCount = function(data) {
  if(typeof data !== "object") {
    return 1;
  } 
  else {
    let count = 0;
    for(let [key, value] of Object.entries(data)) {
      count += getChildrenCount(value);
    };
    return count;
  }
};

const getExtraSpan = function(data) {
  if(typeof data !== "object") {
    return 0;
  } 
  else {
    let count = getDepth(data) ? 1 : 0;
    for(let [key, value] of Object.entries(data)) {
      count += getExtraSpan(value);
    };
    return count;
  }
}

let data = {
  A: {
    B1: {
      C1: {
        D1: "foo"
      },
      C2: {
        D2: "bar",
        D3: "baz"
      }
    },
    B2: {
      C3: {
        D4: "qux"
      },
      C4: {
        D5: "quux"
      }
    }
  }
};

$("tbody").append(getTable(data));
table td,
table th {
  min-width: 2em;
  min-height: 2em;
  border: 1px solid #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<table>
  <tbody></tbody>
</table>
...