Trouble loading data from DataTable

I have a player character who is supposed to read from an external .csv file and populate an array of skills. The .h file features an FTableRowBase subclass to hold the starting data of each skill:

USTRUCT(BlueprintType)
struct FSkillData : public FTableRowBase
{
    GENERATED_USTRUCT_BODY()
    
public:
    
    FSkillData()
    
    : DisplayName (TEXT("DisplayName"))
    
    {}
    
    /** Extra HitPoints gained at this level */
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=SkillData)
    
    FName DisplayName;
    
    /** Icon to use for Achivement */
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=SkillData)
    
    TAssetPtr<UTexture> SkillIcon;
};

In the character’s constructor, the following code does the actual population operation:

// Get data table before assigning values
    
    static ConstructorHelpers::FObjectFinder<UDataTable>SkillData_BP(TEXT("DataTable'/Game/Data/SkillTable.SkillTable'"));
    DataLookupTable = SkillData_BP.Object;
    
    if (DataLookupTable)
    {

        // Key value for each column of values
        static const FString ContextString(TEXT("GENERAL"));
        // o-- Search using FindRow. It returns a handle to the row.
        // Access the variables like GOLookupRow->Blueprint_Class, GOLookupRow->Usecode
    
        FSkillData* GOLookupRow = DataLookupTable->FindRow<FSkillData>(TEXT("DisplayName"), ContextString);
    
        if (GOLookupRow) // Only attempt to populate skills list if valid data is found in the table.
        {
            uint8 SkillsCount = DataLookupTable->GetTableData().Num(); // The skill count will always be far less than 255, otherwise the game will be far too complicated!
        
            for (uint8 i = 0; i < SkillsCount; i++)
            {
                    FSkillData*  SkillData = DataLookupTable->FindRow<FSkillData>(FName(*FString::FromInt(i)), ContextString, true); // TODO: Fix this so that SkillData gets pulled properly
            
                // Do some Stuff with SkillData, but only if it is valid.
            
                if (SkillData)
                {
                    UCharacterSkill* Skill = NewObject<UCharacterSkill>();

                    Skill->SetName(SkillData->DisplayName); // Stick to the first column and get each name under it for each and every skill.
                    Skills.Add(Skill);
                }
            }

        }

When I run the code with breakpoints in Xcode, the for loop that iterates through the skills in the table works in the sense of iterating through SkillCount, but SkillData isn’t being initialized within it. Is there a problem in my code, a problem with how my .csv file is formatted, or a combination of both issues?

For the record, I have a .csv that looks like this:

,,,,,,,,
,Agility,Stealth,Pistols,Assault,Sniping,Hacking,Diplomacy,Engineering
DisplayName,Agility,Stealth,Pistols,Assault,Sniping,Hacking,Diplomacy,Engineering
SkillIcon,""" Texture2D’Game/HUD/Icon.Icon""",""" Texture2D’Game/HUD/Icon.Icon""",""" Texture2D’Game/HUD/Icon.Icon""",""" Texture2D’Game/HUD/Icon.Icon""",""" Texture2D’Game/HUD/Icon.Icon""",""" Texture2D’Game/HUD/Icon.Icon""",""" Texture2D’Game/HUD/Icon.Icon""",""" Texture2D’Game/HUD/Icon.Icon"""
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,
,,,,,,,,

Its formatting is probably way off due to the way it’s formatted in Numbers.

I can see several issues with this, but I cant really say for sure since the formatting is way off, maybe posting the .csv would be better for me to look at. I can’t tell what your list contains, but the way you are trying to find rows needs to have indexis in the beginning (0 first row of data you want to pull, 1 in the second, and so on) , Id also recommend not having assignment in default construction of your FSkillData.

I posed the .csv in the OP, but I’ve since edited it to look like this:

DisplayName,SkillIcon,,,,,,,
Agility,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,,
Stealth,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,,
Pistols,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,,
Assault,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,,
Sniping,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,,
Hacking,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,,
Diplomacy,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,,
Engineering,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,,
,,,,,,,,

Doing so did not solve my problem, since the editor claims that it cannot find the expected column DisplayName.

Also, the constructor code that I have shared is from an ACharacter subclass, not the constructor data for FSkillData. I have not written a constructor for FSkillData because it’s a USTRUCT().

View the data table in the editor and see what it thinks has been imported.

It was importing the stuff wierdly, so I modified the DataTable to look like this:

,DisplayName,SkillIcon,,,,,,
Agility,Agility,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
Stealth,Stealth,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
Pistols,Pistols,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
Assault,Assault,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
Sniping,Sniping,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
Hacking,Hacking,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
Diplomacy,Diplomacy,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
Engineering,Engineering,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
,,,,,,,,

So now it recognizes that there is an element named DisplayName, which was something that I shouldn’t have used in retrospect because the blank cell at the top of the first column automatically becomes the “Name” field. Hence the duplication of the names in question in my new .csv. Unfortunately, it is now failing to find GOLookupRow in the following code, so it’s not even bothering to populate the skills array.

You are on the right path. Find row tries to search for whatever you give it in the FIRST column and gives you that row in a structure. Since you are looking for display name it wont find it. Honestly you do not need the second validation where you search for DisplayName, the first one is fine. You are using a for loop and searching for i, but you have no numerical indeces in the first column. Replace the first column with numbers instead of display name and you should see that it is getting the stuff you want. (Remember to keep the first row first column empty)

Something like this:

,DisplayName,SkillIcon,,,,,,
0,Agility,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
1,Stealth,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
2,Pistols,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
3,Assault,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
4,Sniping,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
5,Hacking,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
6,Diplomacy,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
7,Engineering,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,

I’d remove that header row since the key there will be empty. Maybe the parsing code is smart enough to throw that away, but better to be safe…

I found that the first row, which is not parsed, is good for organization. I also recall (in earlier versions anyway) that they had to match the name of the variables in the structure or else it gives you some warnings.

The .csv file currently looks like this:

,DisplayName,SkillIcon,,,,,,
0,Agility,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
1,Stealth,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
2,Pistols,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
3,Assault,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
4,Sniping,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
5,Hacking,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
6,Diplomacy,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
7,Engineering,""" Texture2D’Game/HUD/Icon.Icon""",,,,,,
,,,,,,,,

Importing the file shows the cells lined up the way they should be.

In addition, the code to read the table has been modified:

// Get data table before assigning values
    
    static ConstructorHelpers::FObjectFinder<UDataTable>SkillData_BP(TEXT("DataTable'/Game/Data/SkillTable.SkillTable'"));
    DataLookupTable = SkillData_BP.Object;
    
    if (DataLookupTable)
    {
        // Key value for each column of values
        static const FString ContextString(TEXT("GENERAL"));
        // o-- Search using FindRow. It returns a handle to the row.
        // Access the variables like GOLookupRow->Blueprint_Class, GOLookupRow->Usecode
    
        FSkillData* GOLookupRow = DataLookupTable->FindRow<FSkillData>(TEXT("DisplayName"), ContextString);
    
        if (GOLookupRow) // Only attempt to populate skills list if valid data is found in the table.
        {
            uint8 SkillsCount = DataLookupTable->GetTableData().Num(); // The skill count will always be far less than 255, otherwise the game will be far too complicated!
        
            for (uint8 i = 0; i < SkillsCount; i++)
            {
                UCharacterSkill* Skill = NewObject<UCharacterSkill>();

                Skill->SetName(GOLookupRow->DisplayName); // Stick to the first column and get each name under it for each and every skill.
                Skills.Add(Skill);
            }

        }
    }

GOLookupRow is returning NULL, which means that the skill array isn’t being populated.

The first column is always the key name, so searching for DisplayName will always fail as there is no key in your set called DisplayName. If you request a row name “5”, you’ll get the row that has Hacking as an entry under the display name

I’ve finally gotten it to load all the skills properly, using the following code:

// Get data table before assigning values
    
    static ConstructorHelpers::FObjectFinder<UDataTable>SkillData_BP(TEXT("DataTable'/Game/Data/SkillTable.SkillTable'"));
    DataLookupTable = SkillData_BP.Object;
    
    if (DataLookupTable)
    {
        // Key value for each column of values
        static const FString ContextString(TEXT("GENERAL"));
        // o-- Search using FindRow. It returns a handle to the row.
        // Access the variables like GOLookupRow->Blueprint_Class, GOLookupRow->Usecode

        uint8 SkillsCount = DataLookupTable->GetTableData().Num(); // The skill count will always be far less than 255, otherwise the game will be far too complicated!
        
        for (uint8 i = 0; i < SkillsCount; i++)
        {
            FString IndexString = FString::FromInt((int32)i);
            FName IndexName = FName(*IndexString);
                
            FSkillData* GOLookupRow = DataLookupTable->FindRow<FSkillData>(IndexName, ContextString);
                
            if (!GOLookupRow)
                break;
                
            UCharacterSkill* Skill = NewObject<UCharacterSkill>();

            Skill->SetName(GOLookupRow->DisplayName); // Stick to the first column and get each name under it for each and every skill.
            Skills.Add(Skill);
        }
        
    }

Many thanks to everyone who commented and helped!