It's not (currently) possible to delete a random row from an ordered array in a gas-efficient way.
There are two commonplace approaches to handling a logical delete. In the case that deletes are few and far between, it can sometimes suffice to set a flag that indicates expired (removed) entries and then logically skip them as one iterates over the list.
struct MyStruct {
uint field1;
uint field2;
bool isActive;
}
MyStruct[] public myStructs;
This maintains the list in the order of declaration but it doesn't control growth of the list.
You can control the growth of the list and do a gas-efficient logical delete at the expense of list order. You move the last item in the list to the row you want to delete. That keeps the gas cost consistent at any scale.

To support that, you need some pointers in the data structure.

In practice, the unordered nature of the list shouldn't be important in most use-cases. A minimalist approach to Smart Contracts inclines to contracts that ensure the integrity of the data. Ordered access is usually a concern that be safely separated from Smart Contract logic.
A more complete walk-through with example implementation here: https://medium.com/@robhitchens/solidity-crud-part-2-ed8d8b4f74ec
Some other simpler examples here: Are there well-solved and simple storage patterns for Solidity?