Как отфильтровать массив машинописного текста на основе, возможно, неопределенного свойства объекта "дата как строка"?

0

Вопрос

API возвращает результат в виде объекта с более чем тысячью значений в следующем формате:

result = {
  "items": [
            {
              "Name": "abc",
              "CreatedOn": "2017-08-29T15:21:20.000Z",
              "Description": "xyz"
            },
            {
              "Name": "def",
              "CreatedOn": "2021-05-21T15:20:20.000Z",
              "Description": "cvb"
            }
          ]
}

Я хотел бы отфильтровать элементы в объекте, которым более 90 дней, без необходимости повторять все элементы, используя цикл for. Другими словами, я хотел бы сделать что-то подобное ниже, но это не работает.

var currentDate = new Date();
var newResult = result.items?.filter(function(i) {
    return ((currentDate - Date.parse(i.CreatedOn)) > 90);
}

В соответствии со свойством IDE CreatedOn имеет тип string | undefined таким образом, он выдает ошибку: Аргумент типа 'string | undefined' не присваивается параметру типа 'string'. Тип 'undefined' не может быть присвоен типу 'string'.

javascript typescript
2021-11-24 03:43:04
3

Лучший ответ

2

Где-то в вашем проекте у вас будет что-то вроде этого:

interface Result {
    readonly items: readonly ResultItem[] | null;
}

interface ResultItem {
    readonly Name       : string;
    readonly CreatedOn  : string | undefined;
    readonly Description: string;
}

или это (или его варианты):

type Result = {
    items?: ResultItem[];
}

interface ResultItem {
    Name       : string;
    CreatedOn? : string;
    Description: string;
}

Или это может быть type вместо того, чтобы interface (просто убедитесь, что вы никогда не используете class чтобы описать данные JSON, как JSON object данные не могут быть class экземпляр, потому что конструктор никогда не запускается).

Кроме того, вы должны использовать camelCase, не PascalCase, для свойств элементов. Поэтому используйте такие имена, как createdOn вместо CreatedOn в сгенерированном вами JSON.

К счастью, вам не нужно менять типы/интерфейсы, просто измените свой текст, чтобы безопасно проверить .CreatedOn и что Date.parse не вернулся NaN. Вот так:

  • То result.items ?? [] отчасти потому, что ваш пост подразумевает result.items является недействительным или, может быть-undefined.
  • Примечание при использовании map с помощью =>-функция стиля, в которую может потребоваться обернуть объектные литералы () таким образом, движок JS не интерпретирует { и } в качестве разделителей блоков.
const result: Result = ...

const currentDate = new Date();

const newResult = (result.items ?? []).filter( e => {
    if( typeof e.CreatedOn === 'string' ) {
        const parsed = Date.parse( e.CreatedOn );
        if( !isNaN( parsed ) ) {
            return ( currentDate - parsed ) > 90;
        }
    }
    return false;
} );

Хотя лично я бы сделал это с начальной filter и map шаги:

const items       = result.items ?? [];
const currentDate = new Date();

const newResult = items
    .filter( e => typeof e.CreatedOn === 'string' )
    .map( e => ( { ...e, CreatedOn2: Date.parse( e.CreatedOn ) } ) )
    .filter( e => !isNaN( e.CreatedOn2 ) )
    .filter( e => ( currentDate - e.CreatedOn2 ) > 90 ) );

или упрощается еще больше:

const items       = result.items ?? [];
const currentDate = new Date();

const newResult = items
    .filter( e => typeof e.CreatedOn === 'string' )
    .map( e => Object.assign( e, { createdOn2: Date.parse( e.CreatedOn ) )
    .filter( e => !isNaN( e.CreatedOn2 ) && ( currentDate - e.CreatedOn2 ) > 90 );

Еще лучшее решение:

  • Если вы контролируете, как создается JSON, вы можете гарантировать, что определенные (или все) свойства элемента всегда будут заданы (и поэтому никогда undefined или null), поэтому, если вы можете гарантировать, что все 3 свойства всегда заданы (никогда null или undefined) затем обновите свои типы/интерфейсы до этого:

    interface ResultItem {
        readonly name       : string;
        readonly createdOn  : string;
        readonly description: string;
    }
    
    • Обратите внимание на camelCase свойства.
    • Неизменность данных-огромное преимущество, поэтому убедитесь, что все свойства вашего интерфейса readonly, все массивы являются readonly T[], и что свойства помечены только ? или | null или | undefined по мере необходимости вместо того, чтобы просто предполагать то или иное.
  • Поэтому убедитесь, что вы используете strictNullChecks В вашем tsconfig.json или tsc варианты! - на самом деле, просто используй strict всегда!

  • Также подумайте об изменении вашего JSON DTO с использования string представление даты (есть ли какие-либо гарантии относительно часового пояса?) чтобы быть изначально читаемой меткой времени Unix (в миллисекундах), таким образом, вы можете избежать проблем с Date.parse полностью:

напр.:

Результат. cs:

public class ResultItem
{
    [JsonProperty( "createdOn" )]
    public DateTimeOffset CreatedOn { get; }

    [JsonProperty( "createdOnUnix" )]
    public Int64 CreatedOnUnix => this.CreatedOn.ToUnixTimeMilliseconds();
}

Результат.ts:

interface ResultItem {
    readonly createdOn    : string;
    readonly createdOnUnix: number;
}
const ninetyDaysAgo = new Date();
ninetyDaysAgo.setDate( ninetyDaysAgo.getDate() - 90 );

const newResult = items.filter( e => new Date( e.createdOnUnix ) < ninetyDaysAgo );

...таким образом,это однострочная работа.


Вышесказанное можно сделать еще проще, поскольку временные метки Unix-это просто целые числа, которые непосредственно сопоставимы, поэтому new Date() можно избежать попадания внутрь filter, вот так:

const ninetyDaysAgo = new Date();
ninetyDaysAgo.setDate( ninetyDaysAgo.getDate() - 90 );
const ninetyDaysAgoUnix = ninetyDaysAgo.getTime();

const newResult = items.filter( e => e.createdOnUnix < ninetyDaysAgoUnix );
2021-11-24 04:21:52
1

Предполагая, что у вас есть интерфейсы, определенные таким образом...

interface Item {
  Name: string,
  Description: string,
  CreatedOn?: string // note the optional "?"
}

interface Result {
  items?: Item[] // also optional according to your code
}

и вы хотите отфильтровать товары старше 90 дней (за исключением тех, у которых нет CreatedOn), тогда попробуйте это

interface ItemWithDate extends Omit<Item, "CreatedOn"> {
  CreatedOn?: Date // real dates, so much better than strings
}

const result: Result = { /* whatever */ }

const items: ItemWithDate[] = result.items?.map(({ CreatedOn, ...item }) => ({
  ...item,
  CreatedOn: CreatedOn ? new Date(CreatedOn) : undefined
})) ?? []

const dateThreshold = new Date()
dateThreshold.setDate(dateThreshold.getDate() - 90)

const newItems = items.filter(({ CreatedOn }) =>
  CreatedOn && CreatedOn < dateThreshold)

Демонстрация игровой площадки с машинописным текстом

2021-11-24 04:01:43

Простите мое невежество (и это довольно большая моя дыра), что делает ({ CreatedOn, ...item }) => ({ сделать, точно? Я никогда не видел оператора распространения ... используется в списке параметров функции одновременно с объектным литералом.
Dai

@Dai извлекает некоторые именованные свойства (CreatedOn) и держит остальное в item. В основном это короткий путь для (obj) => { const { a, b, ...rest } = obj; ...
Phil
-1

код отсутствует ) функции фильтра

var currentDate = new Date();
var newResult = result.items?.filter(function(i) {
    return ((currentDate - Date.parse(i.CreatedOn)) > 90);
}  ) //<= misss


2021-11-24 04:23:03

Ваш ответ не касается tscошибки типа.
Dai

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

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................