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;
