1

I probably need to use other data structure, but I'm stuck with this solution for now. Will appreciate any advice on this.

For now I have this data structure:

const data = [
    {
        id: 'node-1',
        type: 'text',
        data: 'Hello,'
    },
    {
        id: 'node-2',
        type: 'text',
        data: [
            {
                id: 'node-3',
                type: 'text',
                data: ' world.'
            }
        ]
    },
    {
        id: 'node-4',
        type: 'text',
        data: [
            {
                id: 'node-5',
                type: 'text',
                data: [
                    {   
                        id: 'node-6',
                        type: 'text',
                        data: 'Foo bar'
                    }
                ]
            }
        ]
    }
]

I need a function which will give next result:

function split(arr, start_node, start_index, end_node, end_index) { ... }

const { before, range, after } = split(data, 'node-3', 3, 'node-6', 3)

// before
[
    {
        id: 'node-1',
        type: 'text',
        data: 'Hello,'
    },
    {
        id: 'node-2',
        type: 'text',
        data: [
            {
                id: 'node-3',
                type: 'text',
                data: ' wo'
            }
        ]
    },
]

// range
[
    {
        id: 'node-2',
        type: 'text',
        data: [
            {
                id: 'node-3',
                type: 'text',
                data: 'rld.'
            }
        ]
    },
    {
        id: 'node-4',
        type: 'text',
        data: [
            {
                id: 'node-5',
                type: 'text',
                data: [
                    {   
                        id: 'node-6',
                        type: 'text',
                        data: 'Foo'
                    }
                ]
            }
        ]
    }
]

// after
[
    {
        id: 'node-4',
        type: 'text',
        data: [
            {
                id: 'node-5',
                type: 'text',
                data: [
                    {   
                        id: 'node-6',
                        type: 'text',
                        data: ' bar'
                    }
                ]
            }
        ]
    }
]

The problem is to keep nesting structure and to do that effectively. The only solution I came up with was to make this in three different loops, but that obviously ineffective

3
  • 3
    To achieve the desired result, you will need to perform a deep search of the data structure recursively. While it's true that multiple loops may seem inefficient, traversing the data structure in this manner is necessary to get the desired result. Commented Sep 18, 2023 at 18:12
  • 2
    Could you explain the args and return values of split()? I don't quite understand your expected outputs. Commented Sep 18, 2023 at 18:29
  • 1
    What is the relevance of the start_index and end_index parameters? How would they affect the result? Commented Sep 18, 2023 at 18:40

1 Answer 1

1

The code below will find all text nodes in the initial data structure, and will convert each node to a path array that will include all ancestors.

Now, you can more easily split up the list of paths into a list of paths for before, after and within the range.

Finally, take each of those lists of paths, and turn them back into regular objects.

const data = [{"id":"node-1","type":"text","data":"Hello,"},{"id":"node-2","type":"text","data":[{"id":"node-3","type":"text","data":" world."}]},{"id":"node-4","type":"text","data":[{"id":"node-5","type":"text","data":[{"id":"node-6","type":"text","data":"Foo bar"}]}]}]

function split(data, start_node, start_index, end_node, end_index) {

  const paths = [];

  const toPaths = (arr, path=[]) => arr.forEach(({id, type, data}) =>
    Array.isArray(data) ? toPaths(data, [{id, type}, ...path]) : paths.push([{id, type, data}, ...path]))

  toPaths(data)

  const range = (paths, idMatch, index, type, state={inRegion: type==='before'}) =>
    paths.flatMap(([a, ...b]) => {
    if(a.id === idMatch ) {
      state.inRegion = !state.inRegion;
      return [[{
        id: a.id,
        type: a.type,
        data: state.inRegion ? a.data.substring(index) : a.data.substring(0, index)}, ...b]];
    }
    else return state.inRegion ? [[a, ...b]] : [];
  })

  const pathsBefore = range(paths, start_node, start_index, 'before')
  const pathsAfter =  range(paths, end_node, end_index, 'after')
  const pathsDuring = range(range(paths, start_node, start_index, 'after'), end_node, end_index, 'before')

  const build = (paths, arr=[]) => {
    paths.forEach(path => {
      if(path.length) {
        let last = structuredClone(path.pop());
        last = arr.find(({id})=>id===last.id) ?? (arr.push(last), last);
        if(path.length) {
          last.data??=[];
          build([path], last.data);
        }
      }
    })
    return arr
  }

  return {
    before: build(pathsBefore),
    range: build(pathsDuring),
    after: build(pathsAfter),
  }
}

const { before, range, after } = split(data, 'node-3', 3, 'node-6', 3);

console.log('------- before:');
console.log(JSON.stringify(before, null, 2))
console.log('------- range:');
console.log(JSON.stringify(range, null, 2))
console.log('------- after:');
console.log(JSON.stringify(after, null, 2))

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much, paths solution is so clever

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.