Are there build in functions to add new element to TArray and remove last?

Hey guys, say I have a TArray<int> that has 12 elements.

Lets say I now want to add a new int to position zero. Shift all the other elements up and remove the last index.

So basically, every time I add a new element, the array remains the same size and everything gets shifted up one until it get’s deleted

I’ve created this manual version which does what I want:

TArray<float> AFinanceManager::ShiftFloatArray(TArray<float> ArrayToShift)
{
for (int i = ArrayToShift.Num() - 1; i > 0; i–)
{
ArrayToShift* = ArrayToShift[i - 1];
}
ArrayToShift[0] = 0;

return ArrayToShift;

}

Are there any built in functions that do this?

That doesn’t exist, because it would be horribly inefficient. It sounds like what you actually want is a ring buffer or something? What is the purpose of shifting all the elements?

1 Like

So for example, in my financial class, I have a TArray for historic data.

TArray<float> HistoricBuildingIncome

This is initialized to have MAX_MONTH_DATA number of elements. Every month, the oldest data gets deleted, all the rest gets moved and then the new data gets added to 0.

Would it actually be more efficient to do something like

remove last
insert at beginning

That would keep the array size the same, and essentially have the same effect

It’s probably worth noting that this isn’t a function that will be called every frame. Only a few times every in-game month.

TArray have RemoveAt (array.Num()-1) and Insert(item,0) or EmplaceAt(item,0).

Taking the advice to use RemoveAt,Insert, I’ve re-created the function:

TArray<float> AFinanceManager::ShiftMonthlyData(TArray<float> Data)
{
Data.RemoveAt(Data.Last());
Data.Insert(0, 0);

return Data;

}

Why not use std::deque which can remove the last (the oldest data) and also the just inserted data?

And yes, after remove, it is horribly inefficient to shift every single member by one. What some people do is to swap content of ‘to be deleted’ to the last one (yes here, last one mean the just inserted) and then delete this new last one. Voila…this way, no body ever get shifted anywhere. And these functionalities exist in C++ STL (this is where std::deque comes from)

1 Like

#include “Runtime/Core/Public/Containers/Queue.h” TQueue<int> would do it for you. But it wont work as UPROPERTY and it is designed to be thread safe so it likely has significant overhead if you don’t need that.

Alternatively you could wrap TArray for a faster offset based approach:



namespace IndexArray {
    template<typename T, typename Allocator, typename ZeroInt>
    FORCEINLINE bool PushSwap(T & Item, TArray<T, Allocator> &Arr, ZeroInt &Zero) const {
        const int32 Size = Arr.Num();

        if (!Size) {
            return false;
        }

        if (++Zero >= (ZeroInt)Size) {
            Zero %= (ZeroInt)Size;
        }

        T&At = Arr(int32)Zero];

        T Swap = std::move(Item);
        Item = std::move(At);
        At = std::move(Swap);
    }

    template<typename T, typename Allocator, typename ZeroInt>
    FORCEINLINE int32 Add(const T & Item, TArray<T, Allocator> &Arr, ZeroInt &Zero){
        const int32 Size = Arr.Num();
        if (!Size)
        {
            Zero = Arr.Add(Item);
            return 0;
        }
        else if (Size <= Zero)
        {
            Zero %= Size;
        }
        Arr.Insert(Item, Zero++);
        return Size;
    }
};
USTRUCT()
struct FIntQueue {
    GENERATED_BODY();
public:
    UPROPERTY() TArray<int32> Arr;
    UPROPERTY() int32 Zero;

    FORCEINLINE decltype(auto) Num() const { return Arr.Num(); }
    FORCEINLINE decltype(auto) Add(const int32& value) { return IndexArray::template Add<>(value, Arr, Zero); }
    FORCEINLINE decltype(auto) AddRemove(int32& value) { return IndexArray::template PushSwap<>(std::move(value), Arr, Zero); }
    FORCEINLINE decltype(auto) operator ](int32 i)const { return Arr(Zero + i) % Arr.Num()]; }
    FORCEINLINE decltype(auto) operator ](int32 i) { return Arr(Zero + i) % Arr.Num()]; }
};

Have not tested the above code at all, but just giving you an idea of how it would go.