JTech
JTech

Reputation: 3570

How to optimize code that changes a value deeply nested in an object graph

Below is a crude for-loop to illustrate what I need to do.

Basically, if there are any 'Variable' objects with property 'Name' containing the text "TCC#", then I want to change the 'Type' property (not the .Net type) to 'VariableType.Text'.

The code is going to run over 4800 ParsedCard variables and currently takes a stupid amount of time (about 10 minutes) to simply iterate through the list and write a line to the Debug console.

ParsedCard has 
IEnumerable functions which have 
IEnumerable groups which have 
ParseResults which have 
IEnumerable variables

This is such a simple problem but I've tried all sorts of variations using LINQ but can't find anything that performs well (less than 10 seconds).

    private void AdjustTCCVariables(IList<ParsedCard> parsedCards)
    {    
        for (var i = 0; i < parsedCards.Count; i++)
        {
            var parsedCard = parsedCards[i];

            for (var j = 0; j < parsedCard.Functions.Count(); j++)
            {
                var function = parsedCard.Functions.ToList()[j];

                for (var k = 0; k < function.Groups.Count(); k++)
                {
                    var group = function.Groups.ToList()[k];

                    for (var l = 0; l < group.ParseResult.Variables.Count(); l++)
                    {
                        var variable = group.ParseResult.Variables.ToList()[l];

                        if (variable.Name.Contains("TCC#"))
                        {
                            //variable.Type = VariableType.Text;
                            Debug.WriteLine($"Need to change variable at [{i}][{j}][{k}][{l}]");
                        }
                    }
                }
            }
        }
    }

I've tried with this LINQ but it doesn't actually change the 'variable.Type' of the input list (I suspect because it creates a new copy of the objects in memory and the assignment isn't actually affected the 'parsedCards' IEnumerable at all:

    private void AdjustTCCVariables(IEnumerable<ParsedCard> parsedCards)
    {
        var targetVariables =
            parsedCards.SelectMany(x => x.Functions.SelectMany(z => z.Groups))
                .SelectMany(x => x.ParseResult.Variables.Where(v => v.Name.Contains("TCC#")));
        ;

        foreach (var variable in targetVariables)
        {
            variable.Type = VariableType.Text;
        }
    }

Upvotes: 0

Views: 85

Answers (2)

Maarten
Maarten

Reputation: 22955

As mentioned, the bottleneck in your iterations is the .ToList() calls.

Since you mention that you only want to edit the variable.Type property, I would solve this like this.

var variables = from parsedCard in parsedCards
                from function in parsedCard.Functions
                from group in function.Groups
                from variable in group.ParseResult.Variables
                where variable.Name.Contains("TCC#")
                select variable;

foreach (var variable in variables) {
    variable.Type = VariableType.Text;
}

You don't need to know anything other than the variable objects that need changing, you don't need all the indexes and all the other variables. Just select what you need to know, and change it.

This way you will not know the indexes, so your Debug.WriteLine(...); line won't work.

Upvotes: 2

Thorarins
Thorarins

Reputation: 1904

Without knowing what the defintion of the classes , here is some tips. Remove toList, dont count on the iteration (for statement)

int numberOf = parsedCards.Count
 for (var i = 0; i < numberOf; i++)
        {
            //var parsedCard = parsedCards[i];
            int noOf2 = parsedCard[i].Functions.Count()
            for (var j = 0; j < noOf2; j++)
            {
                var function = parsedCard[i].Functions[j];

                int = function.Groups.Count();
                for (var k = 0; k < noOfGroups; k++)
                {
                    var group = function.Groups[k];
                    int noOfVars = group.ParseResult.Variables.Count();
                    for (var l = 0; l < noOfVars; l++)
                    {
                        var variable = group.ParseResult.Variables[l];
                        if (variable.Name.Contains("TCC#"))
                        {
                            //variable.Type = VariableType.Text;
                            Debug.WriteLine($"Need to change variable at [{i}][{j}][{k}][{l}]");
                        }
                    }
                }
            }
        }

Upvotes: 0

Related Questions