Home > Mobile >  How to calculate TVirtualStringTree node data field
How to calculate TVirtualStringTree node data field

Time:01-29

I have VirtualStringTree (VST) with nodes that have the next data structure :

Type
  PMyNodeData = ^TMyNodeData;
  TMyNodeData = record
    Cost:UnicodeString; 
  end;

The Child nodes which they don't have any children has Cost value but any parent nodes has cost value equaled to 0 and am trying to write procedure to make any parent node cost value equaled to sum of all its children cost .

I have tried the next :

procedure CalculateSum(Node:PVirtualNode);
var
  Data: PMyNodeData;
  ChildNode: PVirtualNode;
  ChildData: PMyNodeData;
  CostSum: Extended;
begin
  Data := VST.GetNodeData(Node);
  CostSum := 0.0;
  ChildNode := VST.GetFirstChild(Node);
  while Assigned(ChildNode) do
  begin
    ChildData := VST.GetNodeData(ChildNode);
    CostSum := CostSum   StrToFloat(ChildData.Cost);
    CalculateSum(ChildNode);
    ChildNode := VST.GetNextSibling(ChildNode);
  end;
  if  VST.HasChildren[Node] and (StrToFloat(Data.Cost) = 0) then
    Data.Cost := FloatToStr(CostSum);

end; 

The use :

CalculateSum(vst.RootNode);

But I get Access violation & the sum is not correct .. any suggestions.

CodePudding user response:

You are accessing a child's Cost before it has been calculated. You need to call CalculateSum(ChildNode) before adding its Cost to CostSum, rather than after as you are currently doing.

Also, you did not specify whether PMyDataNode is ever nil, or whether TMyNodeData.Cost contains a blank string for nodes whose value is 0. StrToFloat() will raise an exception if you give it a blank string. If that is a possibility, use StrToFloatDef() or TryStrToFloat() instead.

Try something more like this:

procedure CalculateSum(Node: PVirtualNode);
var
  Data, ChildData: PMyNodeData;
  ChildNode: PVirtualNode;
  Cost, CostSum: Extended;
begin
  if not VST.HasChildren[Node] then Exit;
  Data := VST.GetNodeData(Node);
  if Data = nil then Exit;
  CostSum := 0.0;
  ChildNode := VST.GetFirstChild(Node);
  while Assigned(ChildNode) do
  begin
    ChildData := VST.GetNodeData(ChildNode);
    if ChildData <> nil then
    begin
      CalculateSum(ChildNode);
      if TryStrToFloat(ChildData.Cost, Cost) then
        CostSum := CostSum   Cost;
      // or:
      // CostSum := CostSum   StrToFloatDef(ChildData.Cost, 0.0);
    end;
    ChildNode := VST.GetNextSibling(ChildNode);
  end;
  Data.Cost := FloatToStr(CostSum);
end;

That being said, I would suggest storing TMyNodeData.Cost as a Double/Extended instead of a UnicodeString. All of those conversions are just unnecessary overhead.

Try this instead:

Type
  PMyNodeData = ^TMyNodeData;
  TMyNodeData = record
    Cost: Extended; 
  end;

...

procedure CalculateSum(Node: PVirtualNode);
var
  Data, ChildData: PMyNodeData;
  ChildNode: PVirtualNode;
  CostSum: Extended;
begin
  if not VST.HasChildren[Node] then Exit;
  Data := VST.GetNodeData(Node);
  if Data = nil then Exit;
  CostSum := 0.0;
  ChildNode := VST.GetFirstChild(Node);
  while Assigned(ChildNode) do
  begin
    ChildData := VST.GetNodeData(ChildNode);
    if ChildData <> nil then
    begin
      CalculateSum(ChildNode);
      CostSum := CostSum   ChildData.Cost;
    end;
    ChildNode := VST.GetNextSibling(ChildNode);
  end;
  Data.Cost := CostSum;
end;

CodePudding user response:

I have solved the issue as the next :

    procedure CalculateSum(Node: PVirtualNode);
var
  Data: PMyNodeData;
  ChildNode: PVirtualNode;
  ChildData: PMyNodeData;
  CostSum: Extended;
begin
  Data := VST.GetNodeData(Node);
  if data<>nil then
  CostSum := Data.Cost else Costsum:=0.0;
  ChildNode := VST.GetFirstChild(Node);
  while Assigned(ChildNode) do
  begin
    ChildData := VST.GetNodeData(ChildNode);
    if ChildData <> nil then
    begin
      CalculateSum(ChildNode);
      CostSum := CostSum   ChildData.Cost;
    end;
    ChildNode := VST.GetNextSibling(ChildNode);
  end;
  if (VST.HasChildren[Node]) and (Data<>nil) then
    Data.Cost := CostSum;

end;
  • Related