Unreal and No Raw Loops
I'm first going to look at some of the Sean Parent principles from C++ Seasoning. I'm not going to get into an argument about if these principles are good or not, but simply explore for those interested in using them for their own code.
First I'm going to look at "No Raw Loops", for a good text based background check out here, most solutions are to know your standard library algorithms which provide plenty of ways to transform and the contents of your standard library, but usage of the standard library is discouraged by the Epic Coding Standards and TArray wouldn't work with many of them anyway.
The TLDR; Read the Unreal Container Documentation that use Predicates and the STL algorithm Unreal replacements in the Algo namespace
First thing you should know all that you can do with TArray using predicates. For an example I loaded up an array like so

TArray has the following Predicate methods
bool ContainsByPredicate(Predicate Pred)const
TArray< ElementType > FilterByPredicate(Predicate Pred) const
const ElementType * FindByPredicate(Predicate Pred) const
SizeType Find(ElementType Item)const
SizeType FindByPredicate(Predicate Pred, SizeType Count) const
SizeType FindLastByPredicate(Predicate Pred, SizeType Count) const
The Predicate type would typically be a lambda, but you can also supply a function.


FilterByPredicate will return a sub-Array with elements that match the predicate

Which gives us the output
LogTemp: Apple - Fruit
LogTemp: Orange - Fruit
LogTemp: Bannana - Fruit
LogTemp: Watermellon - Fruit
LogTemp: Plumb - Fruit
Notice here we introduce Algo::ForEach. The Algo folder has a set of template functions similar to the STL, with ForEach probably the most common replacement for Raw Loops.
Equivalent to Array's FilterByPredicate is Algo's CopyIf, which is even more versatile for using any container type that meets the template requirements of iterable input and .Add(<ElementType>) in the output parameter.

Which gives us the output of
LogTemp: Carrot - Vegetable
LogTemp: Onion - Vegetable
LogTemp: Broccoli - Vegetable
LogTemp: Asparagus - Vegetable
Here are the full set of headers, each of which describe an algorithm as of Unreal 5.1 and the documentation for all the functions in the namespace are here

An Algo equivalent of TArray's FilterByPredicate can be done with Algo::CopyIf, which is the Unreal interface version of std::copy_if

Again you can always pass a function to the Algo functions instead of a Lambda, as you can see in LogThing in the above ForEach

You'll need to get to know the needed interfaces for all the Algo functions, for example CopyIf will take any iterable type for the input, but will the output container supports .Add(ElementType).
From the Sean Parent talk you can see that there is a Partition in the Algo library but not a Stable Partition, maybe you can write and share it!
As a quick bonus in case anyone wants help with the question of accessing the index when looping through the array, check out the below link.
Indices https://www.fluentcpp.com/2018/10/26/how-to-access-the-index-of-the-current-element-in-a-modern-for-loop/
And I'll call it here for you to play around more and I hope maybe you can find some opportunities to use less loops and more Algo!
Below is the full example that I ran out of the cheat manager class if you want to try it out
#include "Cheats/MyCheatManager.h"
#include "Algo/Copy.h"
#include "Algo/ForEach.h"
struct FThing
{
FThing(const FString& InThingName, const FString& InThingType) : Name(InThingName), Type(InThingType) {}
FString Name;
FString Type;
};
bool IsThingPlumb(const FThing& InThing)
{
return InThing.Name == FString("Plumb");
}
void LogThing(const FThing& InThing)
{
UE_LOG(LogTemp, Log, TEXT("%s - %s"), *InThing.Name, *InThing.Type);
}
void UMyCheatManager::NoRawLoops()
{
UE_LOG(LogTemp, Log, TEXT("No Raw Loops"));
TArray<FThing> DemoThingArray;
DemoThingArray.Add({ FString("Diplodocus"), FString("Dinosaur") });
DemoThingArray.Add({ FString("Apple"), FString("Fruit") });
DemoThingArray.Add({ FString("Carrot"), FString("Vegetable") });
DemoThingArray.Add({ FString("Onion"), FString("Vegetable") });
DemoThingArray.Add({ FString("Orange"), FString("Fruit") });
DemoThingArray.Add({ FString("Starscream"), FString("Transformer_Deceptacon") });
DemoThingArray.Add({ FString("Bannana"), FString("Fruit") });
DemoThingArray.Add({ FString("Triceratops"), FString("Dinosaur") });
DemoThingArray.Add({ FString("Watermellon"), FString("Fruit") });
DemoThingArray.Add({ FString("Hotrod"), FString("Transformer_Autobot") });
DemoThingArray.Add({ FString("Plumb"), FString("Fruit") });
DemoThingArray.Add({ FString("Broccoli"), FString("Vegetable") });
DemoThingArray.Add({ FString("Astrotrain"), FString("Transformer_Deceptacon") });
DemoThingArray.Add({ FString("Asparagus"), FString("Vegetable") });
//We can use Lambdas for Contain
if (DemoThingArray.ContainsByPredicate([](auto& Thing) {return Thing.Name == FString("Bannana");}))
{
UE_LOG(LogTemp, Log, TEXT("We Have a Bannana"));
}
//Or supply a function
if (DemoThingArray.ContainsByPredicate(IsThingPlumb))
{
UE_LOG(LogTemp, Log, TEXT("We Have a Plumb"));
}
// We can produce a sub array with filter by predicate
TArray<FThing> FruitThings{ DemoThingArray.FilterByPredicate(
[](auto& Thing)
{
return Thing.Type.Equals("Fruit");
})
};
// Iterate through them all using Algo's For Each (like std::for_each in the algorithm STL)
Algo::ForEach(FruitThings,
[](auto& Thing)
{
UE_LOG(LogTemp, Log, TEXT("%s - %s"), *Thing.Name, *Thing.Type);
}
);
TArray<FThing> VegetableThings;
// Alternatively you can use Algo's CopyIf, you can use any output container as long is it support .Add(ElementType), input can be any iterable container
Algo::CopyIf(DemoThingArray, VegetableThings, [](auto& Thing) { return Thing.Type.Equals("Vegetable"); });
// We can also supply a function with ForEach
Algo::ForEach(VegetableThings, LogThing );
}