【问题标题】:Add new dimension dynamically to an array using recursive function使用递归函数向数组动态添加新维度
【发布时间】:2020-09-28 14:09:19
【问题描述】:

我有一个这样的数组:

0: "SuSPENSE"
1: "Subcontractor Expense"
2: "Data Entry"
3: "Design"
4: "Programming"
5: "Subcontractor Expense - Other"
6: "Total Subcontractor Expense"
7: "Technology-Communication"
8: "Licenses"
9: "Domain Hosting Fee"
10: "Internet"
11: "Internet Servers"
12: "Internet - Other"
13: "Total Internet"
14: "Telephone"
15: "Call Center"
16: "Cellular Phones"
17: "Fax"
18: "Telephone - Other"
19: "Total Telephone"
20: "Hardware/Software"
21: "Computer Software"
22: "Hardware/Software - Other"
23: "Total Hardware/Software"
24: "Technology-Communication - Other"
25: "Total Technology-Communication"

这是一个类别和子类别的列表。例如,"Subcontractor Expense" 是一个子类别,以"Total Subcontractor Expense" 结尾。 "Internet""Total Internet" 相同。模板始终相同,类别以“(name)”开头,以“Total (name)”结尾。但是每个类别都可以有许多级别的子类别,就像一棵树。我正在尝试使用递归函数将此数组解析为类似 JSON 的数组或多维数组,但我不知道最大深度是多少。我尝试使用js 执行以下操作:

var parsedArray = [];
var items = getLineItems("Expense", "Total Expense"); //This is a function to return the mentioned array
var newArray = parseArray(items, "Expense");

function parseArray(items, category){
    var lineItems = [];
    for(var i = 0; i < items.length; i++){
        var inArray = $.inArray("Total " + items[i], items);
        if(inArray !== -1){
            parseArray(getLineItems(items[i], "Total " + items[i]), items[i]);
        }
        else {
            lineItems.push(items[i]);
        }
    }
    parsedArray[category] = lineItems;
}

但是这个递归函数永远不会比 2 层更深。有可能生成这样的东西吗?

"SuSPENSE"
"Subcontractor Expense"
    "Data Entry"
    "Design"
    "Programming"
"Subcontractor Expense - Other"
"Technology-Communication"
    "Licenses"
    "Domain Hosting Fee"
    "Internet"
        "Internet Servers"
        "Internet - Other"
    "Telephone"
        "Call Center"
        "Cellular Phones"
        "Fax"
        "Telephone - Other"
    "Hardware/Software"
        "Computer Software"
        "Hardware/Software - Other"
    "Technology-Communication - Other"

【问题讨论】:

  • 很难理解你在这里决定什么应该嵌套在什么下的逻辑。我猜对于每个项目x,您都在寻找相应的项目Total x,并且该对之间的所有内容都应该嵌套在x 下?但是你有 SuSPENSE 只是因为某种原因在顶部孤立了?这似乎是一个非常脆弱的结构。
  • @MattBurland 是的,从 X 到 Total X 的部分应该是嵌套的(并且它内部可能有其他总计)。 SuSPENSE 是单行,没有任何其他字段,因此也不需要 Total SuSPENSE。从线性数组我试图生成一个多维数组,如上所示。最初,数据来自 Excel 文件,它只是一个需要解析的数组,以便正确显示所有内容并保存到数据库中
  • 您期望的输出格式是什么?我了解嵌套和驱动它的规则。但是您在最终结构中寻找什么? [{name: 'SuSPENCE'}, {name: 'Subcontractor Expense', children: [{name: 'Data Entry'}, {name: 'Design'}, {name: 'Programming}]}, {name: 'Subcontractor Expense -Other'}, ...] 之类的东西?还是完全不同的东西?
  • @ScottSauyet 结果应该是这样的数组:[SuSPENSE:“SuSPENSE”,分包商费用:[数据输入:“数据输入”,设计:“设计”,编程:“编程”] , 技术-通讯:[许可证:“许可证”,域名托管费:“域名托管费”,互联网:[互联网服务器:“互联网服务器”,互联网 - 其他:“互联网 - 其他”],电话:[呼叫中心: “呼叫中心”、传真:“传真”]等]]
  • 那不是合法的 JS 格式。你需要一些可以在 JS 中结构化的东西。

标签: javascript jquery arrays recursion


【解决方案1】:

您可以通过检查当前元素是否具有以单词Total 开头的相应元素然后继续当前元素的文本来执行此操作,如果是,则增加级别。当当前元素以单词 total 开头时,您会降低级别。

const data = {"0":"SuSPENSE","1":"Subcontractor Expense","2":"Data Entry","3":"Design","4":"Programming","5":"Subcontractor Expense - Other","6":"Total Subcontractor Expense","7":"Technology-Communication","8":"Licenses","9":"Domain Hosting Fee","10":"Internet","11":"Internet Servers","12":"Internet - Other","13":"Total Internet","14":"Telephone","15":"Call Center","16":"Cellular Phones","17":"Fax","18":"Telephone - Other","19":"Total Telephone","20":"Hardware/Software","21":"Computer Software","22":"Hardware/Software - Other","23":"Total Hardware/Software","24":"Technology-Communication - Other","25":"Total Technology-Communication"}

function toNested(data) {
  const result = [];
  const levels = [result]
  let level = 0;

  const checkChildren = (string, data) => {
    return data.some(e => e === `Total ${string}`)
  }

  data.forEach((e, i) => {
    const object = { name: e, children: []}
    levels[level + 1] = object.children;

    if (e.startsWith('Total')) level--;
    else levels[level].push(object);

    if (checkChildren(e, data.slice(i))) level++;
  })

  return result;
}

const result = toNested(Object.values(data));
console.log(result)

【讨论】:

  • 一个潜在的脆弱性:如果我们在“Fax”之后插入“Total Phone”,它会完全弄乱剩余的嵌套结构。 OP 可能知道这是不可能的,但这是一个潜在的问题。
【解决方案2】:

我还在猜测您的输出格式。这是一个递归解决方案,它给出了我在 cmets 中询问的格式:

[
    {name: "SuSPENSE"},
    {name: "Subcontractor Expense", children: [
        {name: "Data Entry"},
        {name: "Design"},
        {name: "Programming"},
        {name: "Subcontractor Expense - Other"}
    ]},
    {name: "Technology-Communication", children: [
       //...
    ]}
]

const restructure = (
  [s = undefined, ...ss], 
  index = s == undefined ? -1 : ss .indexOf ('Total ' + s)
) => 
  s == undefined
    ? []
    : index > -1
      ? [
          {name: s, children: restructure (ss .slice (0, index))}, 
          ... restructure (ss .slice (index + 1))
        ]
      : [{name: s}, ... restructure (ss)]

const data = ["SuSPENSE", "Subcontractor Expense", "Data Entry", "Design", "Programming", "Subcontractor Expense - Other", "Total Subcontractor Expense", "Technology-Communication", "Licenses", "Domain Hosting Fee", "Internet", "Internet Servers", "Internet - Other", "Total Internet", "Telephone", "Call Center", "Cellular Phones", "Fax", "Telephone - Other", "Total Telephone", "Hardware/Software", "Computer Software", "Hardware/Software - Other", "Total Hardware/Software", "Technology-Communication - Other", "Total Technology-Communication"]

console .log (
  restructure (data)
)
.as-console-wrapper {min-height: 100% !important; top: 0}

请注意,在一种递归情况下,我们调用了主函数两次。一次用于嵌套数据,一次用于数组的其余部分。

另一种可能的结构,我不太喜欢,但可能满足您的需求,如下所示:

[
    "SuSPENSE",
    {"Subcontractor Expense": [
        "Data Entry", "Design", "Programming", "Subcontractor Expense - Other"
    ]},
    { "Technology-Communication": [ 
        // ...
    ]}
]

只需稍作修改即可实现:

const restructure = (
  [s = undefined, ...ss], 
  index = s == undefined ? -1 : ss .indexOf ('Total ' + s)
) => 
  s == undefined
    ? []
  : index > -1
    ? [
        {[s]: restructure (ss .slice (0, index))}, 
        ... restructure (ss .slice (index + 1))
      ]
    : [s, ... restructure (ss)]

更新

这个变体与第一个变体相同,但对于不习惯我的表达方式的人来说可能看起来更熟悉:

const restructure = ([s = undefined, ...ss]) => {
  if (s == undefined) {return []}
  const index = ss .indexOf ('Total ' + s)
  return index < 0
    ? [{name: s}, ... restructure (ss)]
    : [
        {name: s, children: restructure (ss .slice (0, index))}, 
        ... restructure (ss .slice (index + 1))
      ]
}

【讨论】:

  • 非常感谢!不幸的是,我无法标记 2 个正确答案,但您的代码也可以完美运行!
  • 这应该是被接受的答案,它更加清晰和陷阱证明
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-28
相关资源
最近更新 更多