Как удалить пустые / нулевые элементы из массива?

Как я могу удалить пустые элементы или элементы с нулевыми указателями из массива? Общее решение будет приветствоваться.

1 ответ

Решение

Вы можете написать это так:

type
  TArrayHelper = class
    class function RemoveAll<T>(var Values: TArray<T>; const Value: T); static;
  end;

....

function TArrayHelper.RemoveAll<T>(var Values: TArray<T>; const Value: T);
var
  Index, Count: Integer;
  DefaultComparer: IEqualityComparer<T>;
begin
  // obtain an equality comparer for our type T
  DefaultComparer := TEqualityComparer<T>.Default;

  // loop over the the array, only retaining non-matching values
  Count := 0;
  for Index := 0 to high(Values) do begin
    if not DefaultComparer.Equals(Values[Index], Value) then begin
      Values[Count] := Values[Index];
      inc(Count);
    end;
  end;

  // re-size the array
  SetLength(Values, Count);
end;

Предположим, что у вас есть массив указателей:

var
  arr: TArray<Pointer>;

Тогда вы бы удалили nil элементы как это:

TArrayHelper.RemoveAll<Pointer>(arr, nil);

Этот код выбирает легкий путь и всегда использует компаратор по умолчанию. Для более сложных типов это не хорошо. Например, некоторые записи нуждаются в пользовательских компараторах. Вы должны были бы предоставить компаратор, чтобы поддержать это.


Вышеуказанная реализация максимально проста. С точки зрения производительности, это может быть расточительным в вероятном общем сценарии, когда не найдено ни одного подходящего значения, или очень мало. Это потому, что версия выше безоговорочно присваивает, даже если два индекса одинаковы.

Вместо этого, если возникла проблема с производительностью, вы могли бы оптимизировать код, шагая по массиву до первого совпадения. И только тогда начнут двигаться значения.

function TArrayHelper.RemoveAll<T>(var Values: TArray<T>; const Value: T);
var
  Index, Count: Integer;
  DefaultComparer: IEqualityComparer<T>;
begin
  // obtain an equality comparer for our type T
  DefaultComparer := TEqualityComparer<T>.Default;

  // step through the array until we find a match, or reach the end
  Count := 0;
  while (Count<=high(Values)) 
  and not DefaultComparer.Equals(Values[Count], Value) do begin
    inc(Count);
  end;
  // Count is either the index of the first match or one off the end

  // loop over the rest of the array copying non-matching values to the next slot
  for Index := Count to high(Values) do begin
    if not DefaultComparer.Equals(Values[Index], Value) then begin
      Values[Count] := Values[Index];
      inc(Count);
    end;
  end;

  // re-size the array
  SetLength(Values, Count);
end;

Как видите, это гораздо сложнее анализировать. Вы бы только могли подумать о том, чтобы сделать это, если бы оригинальная версия была узким местом.

Другие вопросы по тегам