C # Выберите множество альтернатив в Typescript / Javascript

#javascript #c# #arrays #typescript

#javascript #c# #массивы #typescript

Вопрос:

У меня есть список проектов, и каждый проект содержит список статусов. Мне нужно найти статус с определенным идентификатором во всех проектах.

В C # я могу сделать что-то вроде этого:

 List<StatusResponse> statusList = projects.SelectMany(x => x.Status).ToList();
StatusResponse status = statusList.Find(f => f.Id == id);
 

Я мог бы даже сделать все это в одной строке:

 StatusResponse status = projects.SelectMany(x => x.Status).FirstOrDefault(f => f.Id == id);
 

В Typescript я делаю это:

 private getStatus(statusId: string): StatusResponse {

    for (let project of this.selectedProjects) {

        let status = project.status.find(find => find.id === statusId);
        if (status)
            return status;
    }

    return null;
}
 

Вопрос:

Есть ли более простое решение в Typescript с меньшим количеством кода?

Комментарии:

1. Эквивалентом было бы this.selectedProjects.flatMap(x => x.status).find(f => f.Id == statusId) . Но это собирает все status , а затем использует find . В вашем цикле вы замыкаете короткое замыкание, когда статус найден. Итак, ваш текущий код, вероятно, лучше по производительности

2. Большое спасибо. Да, производительность — это тот момент, который следует учитывать. Но поскольку мои списки очень малы, это не должно быть проблемой при использовании flatMap.

Ответ №1:

Вы могли бы использовать flatMap, как предложил @adiga:

 // >=ES2019
const status = this.selectedProjects.flatMap(pr => pr.status)
                                    .find(stat => stat.id === statusId);
 

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

 function* selectMany<TIn, TOut>(
    source: Iterable<TIn>,
    selector: (item: TIn) => Iterable<TOut>)
: Iterable<TOut> {
    for (const item of source) {
        const subItems = selector(item);
        for (const subItem of subItems) {
            yield subItem;
        }
    }
}

// and while we are at it. Let's make a FirstOrDefault equivalent.

function firstOrNull<T>(source: Iterable<T>, predicate: (item: T) => boolean): T | null {
    for (const item of source) {
        if (predicate(item)) {
            return item;
        }
    }
    return null;
}
 

Пример использования:

 interface Item {
    elements: Array<number>;
}

const items: Item[] = [
    {
        elements: [1, 2, 3]
    },
    {
        elements: [4, 5, 6]
    }
]

const elements = selectMany(items, itm => itm.elements);
for (const el of elements) {
    console.log(el);
}
 

вывод:

 1
2
3
4
5
6

firstOrNull(elements, itm => itm === 6); // 6
firstOrNull(elements, itm => itm === 7); // null
 

Ответ №2:

Что-то вроде

 obj.map((v) => v.status).filter((v) => v.id === id);
 

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

 obj.filter((v) => v.status.id === id);
 

Если вам нужны отдельные объекты, вы можете сделать, например

 obj.map((v) => v.status).find((status) => status.id === id);
 

Комментарии:

1. v.status является массивом