I struggle a bit with deleting struct from my TArray of structs.My struct contains AudioComponent and float.I was using Array.RemoveAt(index), but what i got from this was only removing half of my struct, which is AudioComponent.
Why is that? My function Removing elements looks like this:
void RemoveArrayElement( UAudioComponent AudioComponent )
{
for( int i=0; i<Array.Num(); i++ )
{
if( AudioComponent == Array[i].AudioComponent )
{
Array.RemoveAt( i );
}
}
}
What i want to achieve is completely deleting index, AudioComponent with it's float.
There are few issues with your code. As others mentioned in comments, you should use pointers. And if I'm not mistaken, you aren't allowed to use construction like this:
UPROPERTY()
TArray<UAudioComponent> invalidArray;
You should use UPROPERTY macro, otherwise your properties could and probably will be garbage collected. UPROPERTY wiki.
Next thing is that you are changing array over which you are iterating. I wrote few approaches, let's look at them:
void RemoveArrayElement(UAudioComponent* AudioComponent)
{
TArray<UAudioComponent*> audioArray; // array will be initialized somewhere else, this is for demo purpose.
// you always should check your pointers for validity
if (!AudioComponent || !AudioComponent->IsValidLowLevel() || AudioComponent->IsPendingKill())
return;
// Correct approach 1 (multiple):
TQueue<UAudioComponent*> toDelete;
for (int i = 0; i < audioArray.Num(); i++)
{
auto item = audioArray[i];
if (AudioComponent == item || true) // we simulate another condition for multiselect
{
toDelete.Enqueue(item);
}
}
// better approach for iteration:
for (auto item : audioArray)
if (item == AudioComponent || true) // we simulate another condition for multiselect
toDelete.Enqueue(item);
// finalize deletion in approach 1
UAudioComponent* deleteItem;
while (toDelete.Dequeue(deleteItem))
audioArray.Remove(deleteItem);
// almost correct approach 2 (single) :
UAudioComponent* foundItem;
for (auto item : audioArray)
if (item == AudioComponent)
{
foundItem = item;
break; // we can skip rest - but we must be sure, that items were added to collection using AddUnique(...)
}
if (foundItem)
audioArray.Remove(foundItem);
// correct and the best - approach 3 (single)
audioArray.Remove(AudioComponent);
}
First keep in mind that comparing two objects does not necessarily lead to the expected result of equality. Using the == operator means executing a function (bool operator==(L, R);) that specifies what should happen. So if you did not overload the == operator then you don't know what using it would result to unless you look at the source code where it's defined. Since you want to remove the exact audio component and not an instance of it that looks the same, you want to use pointers in your array. That also helps performance since your are not copying the whole component when calling RemoveArrayElement(...); but a single pointer. Also when there are two identical audio components stored in the array and they are at index a and a+1, then removing the audio component at index a the next iteration would skip your second audio component since all upper indexes are decremented by one.
Related
I have a list, each element of which contains an inner list.
I need to get an element of the outer list, in which the specified condition is fulfilled for the inner list at least once.
I wrote code like this:
outerList?.find {
!it.items.isNullOrEmpty() && it.items?.any { item ->
item.isVisible == visibility &&
item.progress == currentProgress
} == true
}?.let { outerItem ->
currentItem = outerItem
// here some logic
} ?: run {
currentItem = null
// here some logic
}
But I'm not sure if this code is efficient. Perhaps you should use sequences instead of a list?
Can you please tell me which solution will be the most efficient in terms of execution time and memory consumption for my case?
A sequence isn't going to help here, because you are only performing one operation on the outer list, and one operation on the inner list.
Your !it.items.isNullOrEmpty() check is entirely redundant to the ?.any you follow it up with, so you can remove that. Perhaps the cleanest way to write this would be:
outerList?.find {
it.items.orEmpty().any { item ->
item.isVisible == visibility && item.progress == currentProgress
}
}
Never do this: ?.let { ... } ?: run { }. Aside from being very poor for readability, it is error-prone. If you accidentally return null from the let block, the run block will also be run unexpectedly.
I have a c++ code that at one part it stored some values of a measurement in a vector and this vector is a part of set of data schema which is serialized and then sent to a streamer.
There is new requirement that for a specific case I need just one value of the measurement which is always rewritten with the latest one, but I don't want to change the vector variable in order to keep the same schema. So I thought that for that case to rewrite each time the first element of the vector, something like this
vector<int> store_measurements;
int measurement = 10;
if (condition == "several_values")
{
store_measurements.pushback(measurement);
}
else
{
store_measurements.at(0) = measurement ;
}
It seems to work fine when the vector is not cleared, but I'd like to ask if this is the correct way to do that or there is a more preferable way to do it?
You can use the front() function.
vector<int> store_measurements;
int measurement = 10;
if (condition == "several_values")
{
store_measurements.push_back(measurement);
}
else
{
store_measurements.resize(1);
store_measurements.front() = measurement ;
}
Edit:
Based on the comments I added store_measurements.resize(1); before the assignment
I would probably use assign() which replaces all the values in the vector like this:
if (condition == "several_values")
{
store_measurements.push_back(measurement);
}
else
{
store_measurements.assign(1, measurement);
}
I'm trying to sort a vector of items. As mentioned in the code comments, the ordering should be:
Participants with more action points (mAp) go first. When there is a tie, participants with the same disposition (mDisposition) as the initiator of the battle (mBattleInitiator) go first.
The following code (simplified example) crashes on macOS, presumably due to my sort implementation being incorrect:
#include <QtCore>
class AiComponent
{
public:
enum Disposition {
Friendly,
Hostile
};
AiComponent(Disposition disposition) : mDisposition(disposition) {}
~AiComponent() { qDebug() << "Destroying AiComponent"; }
Disposition mDisposition;
};
class BattleManager
{
public:
BattleManager() : mBattleInitiator(AiComponent::Hostile) {}
class Turn {
public:
Turn() : mAp(1) {}
Turn(QSharedPointer<AiComponent> aiComponent) :
mAiComponent(aiComponent),
mAp(1)
{
}
Turn(const Turn &rhs) :
mAiComponent(rhs.mAiComponent),
mAp(1)
{
}
QSharedPointer<AiComponent> mAiComponent;
int mAp;
};
void addToTurnQueue(QSet<QSharedPointer<AiComponent>> aiComponents);
AiComponent::Disposition mBattleInitiator;
QVector<Turn> mTurnQueue;
Turn mActive;
};
void BattleManager::addToTurnQueue(QSet<QSharedPointer<AiComponent> > aiComponents)
{
foreach (auto aiComponent, aiComponents) {
mTurnQueue.append(Turn(aiComponent));
}
// Sort the participants so that ones with more action points (mAp) go first.
// When there is a tie, participants with the same disposition (mDisposition)
// as the initiator of the battle (mBattleInitiator) go first.
std::sort(mTurnQueue.begin(), mTurnQueue.end(), [=](const Turn &a, const Turn &b) {
if (a.mAp > b.mAp)
return true;
if (a.mAp < b.mAp)
return false;
// At this point, a.mAp is equal to b.mAp, so we must resolve the tie
// based on mDisposition.
if (a.mAiComponent->mDisposition == mBattleInitiator)
return true;
if (b.mAiComponent->mDisposition == mBattleInitiator)
return false;
return false;
});
}
int main(int /*argc*/, char */*argv*/[])
{
BattleManager battleManager;
for (int i = 0; i < 20; ++i) {
qDebug() << "iteration" << i;
QSet<QSharedPointer<AiComponent>> participants;
AiComponent::Disposition disposition = i % 2 == 0 ? AiComponent::Hostile : AiComponent::Friendly;
QSharedPointer<AiComponent> ai(new AiComponent(disposition));
participants.insert(ai);
battleManager.addToTurnQueue(participants);
}
// This should print (1 1), (1 1), ... (1 0), (1 0)
foreach (auto turn, battleManager.mTurnQueue) {
qDebug() << "(" << turn.mAp << turn.mAiComponent->mDisposition << ")";
}
return 0;
}
I've looked into other answers on the topic. Most of them just say "implement it as a > b", which won't work in my case. There are a few that seem relevant but don't help me:
https://stackoverflow.com/a/16824720/904422 - says that other standard algorithms will work but doesn't mention any concrete examples
https://stackoverflow.com/a/33508373/904422 - confusing, seems like overkill
What is the simplest way to achieve what I'm after?
The reason for the crash hasn't been explained yet. Most implementations of std::sort are based on quick sort, specifically Hoare partition scheme, which scans an array from left towards the right as long as element values < pivot values, and scans the array from right towards the left as long as element values > pivot values. These scans are counting on the fact that finding an element value = pivot value will stop a scan, so there's no check for scanning beyond the boundaries of an array. If the user supplied less than compare function returns true in the case of equal elements, then either of the scans may go beyond the array boundaries and cause a crash.
In the case of a debug build, testing of the user compare function may be done to ensure that the compare is less than and not less than or equal, but for a release build, the goal is speed, so these checks are not performed.
I'll just go off of the comment in your code and explain what's wrong with it (if anything), and how you would fix it.
// Sort the participants so that ones with more action points (mAp) go first.
Good so far
// When there is a tie, participants with the same disposition (mDisposition) as the initiator of the battle (mBattleInitiator) go first.
What if both participants have the same disposition as the initiator? Even if you can guarantee that no 2 elements will satisfy this condition, the sort algorithm is allowed to compare an element against itself. This test would return true in that case, violating one of the conditions of a strict-weak ordering, which is that an element must compare equal to itself (i.e. compare(a,a) must always be false).
Perhaps instead you want to say that if a has the same disposition as the initiator, and b does not, then a should be considered less than b. This can be encoded as:
return dispositionOfA == mBattleInitiator && dispsitionOfB != mBattleInitiator;
So your full test would look like this:
if (a.mAp > b.mAp)
return true;
if (a.mAp < b.mAp)
return false;
return a.mAiComponent->mDisposition == mBattleInitiator &&
b.mAiComponent->mDisposition != mBattleInitiator;
Is there an efficient way to have a single iterator iterate on the concatenation of 2 objects vector, as if they were one?
The two vectors contain the same data type of course.
UPDATE:
I think I should have put more details about my question and my context. This may answer some of the questions:
In fact I am having one attribute that store the last position of that iterator and inside one method I start to iterate from the last position where I stopped in the previous call, which might be in the first vector or in the second one.
What about this solution? It may be not elegant, but I guess it respects the standard. right?
vector<Whatever>::iterator it = vectorA.begin();
bool loopOnVectorA = true;
while(true) {
// My stuff here
if (loopOnVectorA && it == vectorA.end())
{
it = vectorB.begin();
loopOnVectorA = false;
}
else if (it == vectorB.end())
{
break;
}
else
{
varPtrIt++;
}
}
I am creating a program that finds the shortest path from one vertex to another that is based upon a set and tables that keep track vertex information as well as the shortest paths from one vertex to another. This is created using an array, NOT a linked list.
//--------------------------------------------------------------------------
// 'updatepaths' uses the newest vertex which was added to the Set to modify
// the distances of the remaining vertices (if smaller)
// in addition to the newly added vertex, it uses the Set, the Vertexinfo
// and the Shortpath tables
//--------------------------------------------------------------------------
void Paths::updatepaths(int addnode)
{
if (done.in(addnode)) //done is a set instance, checks if the value is in the set
{
for (int i = 0; i<VERTICES; i++)
{
if (shortpath[edgedata[addnode].path[i].vertexto].distance > edgedata[addnode].path[i].weight) //HERE IS THE ISSUE
{
shortpath[edgedata[addnode].path[i].vertexto].distance = edgedata[addnode].path[i].weight;
shortpath[edgedata[addnode].path[i].vertexto].via = addnode;
}
}
}
}
I realize that the code is quite difficult to read, but it's the only way to compare vertex distances to one another that I can think of -- the issue is that in the if statement, sometimes it will try to compare values that don't exist in the array.
For example, edgedata[addnode].path[0].weight may contain NO VALUE - thus my program throws an access violation (segmentation fault). I tried setting edgedata[addnode].path[i].weight != NULL in the if statement as well as 0, but you cannot use NULL during arithmetic and it won't ever be 0 if it doesn't exist.
How should I make it so that it won't try to compare values that don't exist? Thanks for the help.
If your logic is regularly hitting NULL objects, there may likely be larger design or implementation issues in your code, but the easiest thing to do here to patch over your immediate problem is to use std::array<>::at(), instead of std::array<>::operator[], and catch the out_of_range exceptions it can generate:
try {
if (shortpath.at(edgedata.at(addnode).path.at(i).vertexto).distance >
edgedata.at(addnode).path.at(i).weight)
{
shortpath.at(edgedata.at(addnode).path.at(i).vertexto).distance =
edgedata.at(addnode).path.at(i).weight;
shortpath.at(edgedata.at(addnode).path.at(i).vertexto).via = addnode;
}
}
catch (std::out_of_range const &oor) {
std::cerr << "Out of Range error: " << oor.what() << std::endl;
}
Alternately, you can short-circuit checks in your if statement along these lines (I probably missed a check or two here, so watch out):
if ((edgedata.size() >= addnode)
&& (edgedata[addnode].path.size() >= i)
&& (shortpath.size() >= edgedata[addnode].path[i].vertexto)
&& (shortpath[edgedata[addnode].path[i].vertexto].distance >
edgedata[addnode].path[i].weight))
{
shortpath[edgedata[addnode].path[i].vertexto].distance =
edgedata[addnode].path[i].weight;
shortpath[edgedata[addnode].path[i].vertexto].via = addnode;
}
When you write c++ code, you shoud use the c++ coding style firstly. For example, you can use the std::vector instead of array, then you shoud use the iterator to access the element of vector or use vec.at(i) because this two methods can check the boundary exceeded, In c,it has no way to do so