1

I am working on a vue js app and i have the following recursive array :

[
        {
            id: 1,
            name: "sam",
            list: [
                {
                    id: 2,
                    name: "john",
                    list: []
                },
                {
                    id: 6,
                    name: "sarah",
                    list: [
                        {
                            id: 21,
                            name: "fadi",
                            list: []
                        }
                    ]
                }
            ]
        },
        {
            id: 3,
            name: "ross",
            list: [
                {
                    id: 4,
                    name: "maya",
                    list: []
                }
            ]
        },
        {
            id: 5,
            name: "steph",
            list: []
        },
        {
            id: 7,
            name: "joseph",
            list: []
        },
    ],

So i have the id of the object that needs moving and the id of where it should be moved for example i get idmoving=6 and destination = 2 so the object of id 6 with its content should move to the list of the object of id 2 the list becomes the following:

    [
        {
            id: 1,
            name: "sam",
            list: [
                {
                    id: 2,
                    name: "john",
                    list: [
                {
                    id: 6,
                    name: "sarah",
                    list: [
                        {
                            id: 21,
                            name: "fadi",
                            list: []
                        }
                    ]
                }
            ]
                },
            ]
        },
        {
            id: 3,
            name: "ross",
            list: [
                {
                    id: 4,
                    name: "maya",
                    list: []
                }
            ]
        },
        {
            id: 5,
            name: "steph",
            list: []
        },
        {
            id: 7,
            name: "joseph",
            list: []
        },
    ],

I'm sorry i'm still new to this i hope someone can help with a function that can do that.

0

2 Answers 2

3

You could get the source array and index of the wanted node and the target object in advance and splice the array and push the spliced array to the target array.

This approach takes a depth first search and exit the recursion if both nodes are found.

const
    move = (data, from, to) => {
        const
            search = array => array.some((o, index) => {
                if (o.id === from) source = { array, index };
                else if (o.id === to) target = o;
                return source && target || search(o.list);
            });
        let source, target;

        if (!search(data)) return;
        target.list.push(...source.array.splice(source.index, 1));
    },
    data = [{ id: 1, name: "sam", list: [{ id: 2, name: "john", list: [] }, { id: 6, name: "sarah", list: [{ id: 21, name: "fadi", list: [] }] }] }, { id: 3, name: "ross", list: [{ id: 4, name: "maya", list: [] }] }, { id: 5, name: "steph", list: [] }, { id: 7, name: "joseph", list: [] }];

move(data, 6, 2);
       
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

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

1 Comment

this is imperative form at its very finest. a true inspiration, nina
3

I would probably write this atop some reusable helper functions. Moving a value is a matter of finding it in one location, removing it from there, and adding it to a new location. So I would write a move function that uses find, remove, and add functions.

Each can be done with some fairly simple recursion. Here's one approach:

// Helper functions
const findById = (id, xs) => 
  xs .reduce (
    (r, x) => r != null ? r : x.id == id ? x : findById (id, x .list) || null,
    null
  )

const removeById = (id, xs) =>
  xs .reduce (
    (r, x) => x.id === id ? r : [... r, {... x, list: removeById (id, x .list)}], 
    [] 
  )

const addById = (id, newVal, xs) =>
  xs .reduce (
    (r, x) => [... r, {... x, list: [... addById (id, newVal, x .list), ... (x.id == id ? [newVal] : [])]}],
    []
  )

// Main function
const moveById = (id, toId, data) => 
  addById (toId, findById (id, data), removeById (id, data))


// Sample data
const data = 
  [{id: 1, list: [{id: 2, list: [], name: "john"}, {id: 6, list: [{id: 21, list: [], name: "fadi"}], name: "sarah"}], name: "sam"}, {id: 3, list: [{id: 4, list: [], name: "maya"}], name: "ross"}, {id: 5, list: [], name: "steph"}, {id: 7, list: [], name: "joseph"}]

// Demo
console .log (moveById (6, 2, data))
.as-console-wrapper {max-height: 100% !important; top : 0}

This is not complete. Presumably, there needs to be some sort of error-checking about whether a value has been found before we try to add it.

I would actually write these helpers in a more generic fashion, using an arbitrary predicate for input rather than an id. If they were curried, then we could easily create findById by passing the result of applying an id value to a function like (id) => (x) => x.id == id to create a predicate. So in practice, I might write something more like:

const recursiveFind = (pred) => (xs) => 
  xs .reduce (
    (r, x) => r != null ? r : pred (x) ? x : recursiveFind (pred) (x .list || []) || null,
    null
  )

const findById = (id) => 
  recursiveFind (x => x.id === id)

And something similar for remove and add.

These become useful with, say, recursiveRemove ((x) => x.id > 5) (data), which would remove all elements at any level of nesting with ids greater than 5.

And I might go still further to make your list property parameterizable. But you can see that approach in another SO answer.

2 Comments

Instead of one behemoth function, we get four smaller reusable ones :D
How would you write the removeById , addById and moveById in a more generic way? @Scott Sauyet

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.