Home > Software engineering >  Recursion mapping issue
Recursion mapping issue

Time:04-06

I have a self referencing nth depth structure. I want to map this structure into a list of strings. But i cannot seem to have any success in doing it.

    public class SPFolderStructure
    {
        public string Name { get; set; }
        public List<SPFolderStructure> Structure { get; set; }
    }

So the structure can be like this:

[
  {
    "Name": "General",
    "Structure": [
      {
        "Name": "Important",
        "Structure": [
          {
            "Name": "Readings",
            "Structure": []
          },
          {
            "Name": "Pictures",
            "Structure": []
          },
          {
            "Name": "Support",
            "Structure": []
          },
          {
            "Name": "RandomStuff",
            "Structure": []
          },
          {
            "Name": "Money",
            "Structure": []
          },
          {
            "Name": "Gifs",
            "Structure": []
          },
          {
            "Name": "Cells",
            "Structure": []
          }
        ]
      }
    ]
  },
  {
    "Name": "Alert",
    "Structure": [
      {
        "Name": "Important",
        "Structure": [
          {
            "Name": "Readings",
            "Structure": []
          },
          {
            "Name": "Pictures",
            "Structure": []
          },
          {
            "Name": "Support",
            "Structure": []
          },
          {
            "Name": "RandomStuff",
            "Structure": []
          },
          {
            "Name": "Money",
            "Structure": []
          },
          {
            "Name": "Gifs",
            "Structure": []
          },
          {
            "Name": "Cells",
            "Structure": []
          }
        ]
      }
    ]
  }
]

what this should result in is 14 strings

  • /General/Important/Readings

  • /General/Important/Pictures

  • /General/Important/Support

  • /General/Important/RandomStuff

  • /General/Important/Money

  • /General/Important/Gifs

  • /General/Important/Cells

  • /Alert/Important/Readings

  • /Alert/Important/Pictures

  • /Alert/Important/Support

  • /Alert/Important/RandomStuff

  • /Alert/Important/Money

  • /Alert/Important/Gifs

  • /Alert/Important/Cells

I have tried recursion, and have had small success with simple structures, but with complex ones such as this, no luck.

        public static void DecodeFolderStructure(List<SPFolderStructure> list, List<string> result, string baseName = null, string fullPath = null) {
            for (var i = 0; i != list.Count; i  ) {
                if (string.IsNullOrEmpty(fullPath) && !string.IsNullOrEmpty(baseName)) {
                    fullPath  = $"/{baseName}";
                }

                var item = list[i];
                item.Name = item.Name.Trim();
                if (baseName != item.Name) {
                    fullPath  = $"/{item.Name}";
                }

                if (item.Structure is {Count: > 0}) {
                    DecodeFolderStructure(item.Structure, result, baseName, fullPath);
                    fullPath = null;
                }

                if (fullPath == null) {
                    continue;
                }

                result.Add(fullPath);
                fullPath = null;
            }
        }

CodePudding user response:

Your structure is just fine and also the class you created is an exact match for the JSON string. You can simply deserialize the string into an object. Next you need recursion to print out the result as done below.

Note: I used an override of the ToString() in your class for the output. You don't have to do that but it is also very handy when you are debugging.

string json = @"[
  {
    'Name': 'General',
    'Structure': [
      {
        'Name': 'Important',
        'Structure': [
          {
            'Name': 'Readings',
            'Structure': []
          },
          {
    'Name': 'Pictures',
            'Structure': []
          },
          {
    'Name': 'Support',
            'Structure': []
          },
          {
    'Name': 'RandomStuff',
            'Structure': []
          },
          {
    'Name': 'Money',
            'Structure': []
          },
          {
    'Name': 'Gifs',
            'Structure': []
          },
          {
    'Name': 'Cells',
            'Structure': []
          }
        ]
      }
    ]
  },
  {
    'Name': 'Alert',
    'Structure': [
      {
        'Name': 'Important',
        'Structure': [
          {
            'Name': 'Readings',
            'Structure': []
          },
          {
            'Name': 'Pictures',
            'Structure': []
          },
          {
            'Name': 'Support',
            'Structure': []
          },
          {
            'Name': 'RandomStuff',
            'Structure': []
          },
          {
            'Name': 'Money',
            'Structure': []
          },
          {
            'Name': 'Gifs',
            'Structure': []
          },
          {
            'Name': 'Cells',
            'Structure': []
          }
        ]
      }
    ]
  }
]";

// Deserializing the string into a list of SPFolderStructure objects.

List<SPFolderStructure> result = Newtonsoft.Json.JsonConvert.DeserializeObject<List<SPFolderStructure>>(json);

// Now call the recursive routine to print out the result.
DecodeFolderStructure(string.Empty, result);


void DecodeFolderStructure(string parent, List<SPFolderStructure> list)
{
    if(list != null)
        foreach (SPFolderStructure item in list)
        {
            var dir = $"{parent}{item}";

            Console.WriteLine(dir);

            // Recursion here. Call your own routine again.
            DecodeFolderStructure(dir, item.Structure);
        }
}

Console.ReadKey();


public class SPFolderStructure
{
    public string Name { get; set; }
    public List<SPFolderStructure> Structure { get; set; }
    public override string ToString()
    {
        return $"/{Name}";
    }
}

And here is the result:

/General
/General/Important
/General/Important/Readings
/General/Important/Pictures
/General/Important/Support
/General/Important/RandomStuff
/General/Important/Money
/General/Important/Gifs
/General/Important/Cells
/Alert
/Alert/Important
/Alert/Important/Readings
/Alert/Important/Pictures
/Alert/Important/Support
/Alert/Important/RandomStuff
/Alert/Important/Money
/Alert/Important/Gifs
/Alert/Important/Cells

CodePudding user response:

Here's one approach. It makes use of a local function to hide the recursive part with the path 'accumulator'. It only returns the paths for the leaf nodes per your list:

private static IEnumerable<string> DecodeFolderStructure(IEnumerable<SPFolderStructure> structures)
{
    return structures.SelectMany(s => GetPaths(s, Enumerable.Empty<string>()));

    IEnumerable<string> GetPaths(SPFolderStructure structure, IEnumerable<string> path)
    {
        path = path.Concat(new[] { structure.Name });
        return structure.Structure.Any()
            ? structure.Structure.SelectMany(s => GetPaths(s, path))
            : new[] { string.Join("/", path) };
    }
}

See this fiddle for a working demo.

CodePudding user response:

You can use LINQ with recursion to convert each level:

public static class ListExt {
    public static List<string> ToStrings(this List<SPFolderStructure> src)
        => src.SelectMany(s => s.Structure.Count > 0
                                    ? s.Structure.ToStrings().Select(s2 => $"/{s.Name}/{s2}")
                                    : new[] { $"/{s.Name}" })
              .ToList();
}
  • Related