Home > Software design >  How to recursively traverse array and replace certain values?
How to recursively traverse array and replace certain values?

Time:12-10

I'm trying to parse a JSON structure, The json structure looks something like this:

{
    children: [
        {
            type: "p",
            children: [{
                text: ""
            }]
        },
        {
            type: "social_embed",
            children: [{
                text: ""
            }]
            source_url: "some_url"
        },
        {
            type: "p",
            children: [{
                type: "p",
                children: [{
                    type: "p",
                    children: [{
                        text: ""
                    }]
                }]
            }]
        },
    ]
}

The output will look like:

{
    children: [
        {
            type: "p",
            children: [{
                text: ""
            }]
        },
        {
            type: "p",
            children: [{
                text: "some_url"
            }]
        },
        {
            type: "p",
            children: [{
                type: "p",
                children: [{
                    type: "p",
                    children: [{
                        text: ""
                    }]
                }]
            }]
        },
    ]
}

This is the code I'm trying:

 if (currentBlock.type == "card" || currentBlock.type =="card_body") 
       {
          parsedBlocks.map((block: any, index: any) => {
            block.children = parseBlocks(block.children)
          })

          console.log("Blocks after parsing", parsedBlocks)

          editor.insertFragment(parsedBlocks);
          return true
        }

        const parseBlocks = (blocks: any): any => {

        blocks.forEach((block: any) => {
          console.log("Block ", block)
          if (block.type == "social_embed") {
            const newBlock = {
                type: "p",
                children: [
                  {
                    text: block.source_url
                  }
                ]
              }
              blocks[blocks.indexOf(block)] = newBlock
          }
          if (block.children) {
            return parseBlocks(block.children)
          }
        })
        return blocks
      }

I want to recursively traverse all children until there is no children property in the object and when I encounter the object with type: "social_embed" I want to replace it with type : "p" and the text as source_url and modify the entire array, The children can have unlimited nesting but the social_embed can't have anything inside it's children other than {text: ""}

CodePudding user response:

You could map new object to the children and take either a new object of the old one.

const
    update = ({ children = [], ...object }) => {
        if (object.type === "social_embed") {
            const
                type= 'p',
                text = object.source_url;
            return { type, children: [{ text }] };
        }
        children = children.map(update);
        return children.length
            ? { ...object, children }
            : object;
    },
    tree = { children: [{ type: "p", children: [{ text: "" }] }, { type: "social_embed", children: [{ text: "" }], source_url: "some_url" }, { type: "p", children: [{ type: "p", children: [{ type: "p", children: [{ text: "" }] }] }] }] };

tree.children = tree.children.map(update);

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

CodePudding user response:

How about something like this:

const parse = node => {
  if (node.type === "social_embed") {
    return {
      type: "p",
      children: [{ text: node.source_url}]
    }
  }

  return node.children ? {
    ...node,
    children: node.children.map(parse)
  } : node;
}

https://replit.com/@jamiedixon/ParseTree#index.js

If you want to go one step further you could define visitors for the nodes based on the type and process them that way.

const socialEmbed = node => ({
  type: "p",
  children: [{ text: node.source_url }]
})

const visitors = {
  "social_embed": [socialEmbed]
}

const parse = node => {
  const _visitors = visitors[node.type] || [x => x];
  const result = _visitors.reduce((agg, fn) =>  fn(agg), node);

  return result.children ? {
    ...result,
    children: result.children.map(parse)
  } : result;
}

https://replit.com/@jamiedixon/ParseTree#visitors.js

CodePudding user response:

you can do a recurcive function which loops over your tree and modify in place when it encounters a social_embed

const input = {children: [{type: "p",children: [{text: ""}]},{type: "social_embed",children: [{text: ""}], source_url: "some_url"},{type: "p",children: [{type: "p",children: [{type: "p",children: [{text: ""}]}]}]}]}

function rec(input) {
    if (input.type === "social_embed") {
        input.children = [{text: input.source_url}]
        input.type = "p"
        delete input.source_url
    }
    input.children?.forEach(rec)
}
rec(input)

console.log(JSON.stringify(input, null, 4))
.as-console-wrapper { max-height: 100% !important; top: 0; }

If you prefer to generate a new object and keep the original one intact you can do it like this

const input = {children: [{type: "p",children: [{text: ""}]},{type: "social_embed",children: [{text: ""}], source_url: "some_url"},{type: "p",children: [{type: "p",children: [{type: "p",children: [{text: ""}]}]}]}]}

function rec(input) {
  const output = {...input}
    if (input.type === "social_embed") {
        output.children = [{text: input.source_url}]
        output.type = "p"
    delete output.source_url
    } else if (input.children) {
    output.children = input.children.map(rec)
  }
  return output
}
const output = rec(input)

console.log(JSON.stringify(output, null, 4))
.as-console-wrapper { max-height: 100% !important; top: 0; }

  • Related