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

Я пытаюсь сделать таблицу, в которой перечислены 2 типа файлов, связанных вместе. Один тип — это файлы .mp3, а другой — файлы .txt. Я хочу, чтобы эти файлы были связаны друг с другом, чтобы файлы с одинаковым именем делили одну строку, когда через них проходит цикл foreach. Это делается для того, чтобы можно было воспроизводить файлы mp3 и открывать соответствующий текстовый файл.

На странице App.razor есть таблица, в которой отображаются все файлы в папке, но не учитывается, имеют ли файлы двух типов одно и то же имя. Может ли кто-нибудь помочь с тем, как создать класс, в котором файлы связаны вместе, чтобы их можно было вызывать в таблице?

Вот код.

<table class = "table table-striped mb-0">
    <thead>
    <tr>
        <th scope = "col">Name</th>
        <th scope = "col">Actions</th>
    </tr>
    </thead>
    <tbody>
        @foreach (var file in textList)
        {
            <tr>
                <td>
                    @file.Name
                </td>
                <td>
                    <span @onclick = "() => PlayAudio(file.Url)"
                          class = "text-primary oi oi-play-circle me-2" aria-hidden = "true" role = "button">
                    </span>
                    <span @onclick = "() => DeleteAudio(file)"
                          class = "text-danger oi oi-trash" aria-hidden = "true" role = "button">
                    </span>
                    <span @onclick = "() => openTextFile(file)"
                          ><button>Open</button>
                    </span>
                </td>
            </tr>
        }
    }
    </tbody>
</table>
@code{
    readonly List<TextFile> textList = new();
    readonly string FolderName = "textSoundFiles";

    protected override void OnInitialized()
    {
        var path = $"{env.WebRootPath}\\{FolderName}\\";
        var files = new DirectoryInfo(path).GetFiles();

        foreach (var file in files)
        {
            textList.Add(new TextFile
            {
                Name = file.Name,
                Url = $"/textFiles/{file.Name}",
                Path = file.FullName
            });
        }
    }

    public class TextFile
    {
        public string Name { get; set; }
        public string Url { get; set; }
        public string Path { get; set; }
    }
}

Я пытаюсь создать таблицу со списком файлов, которая позволяет мне воспроизводить аудио из перечисленных аудиофайлов. Я пытаюсь 2 типа файлов в каталоге, так что файлы с одинаковыми именами (за исключением их типа MIME) связаны друг с другом

Повторить files.GroupBy(file => file.Name)

Brian Parker 21.11.2022 15:14

@BrianParker, как это свяжет файлы вместе?

Addlon 21.11.2022 15:32

Они будут сгруппированы по имени, и вы будете перебирать группы. Сами группы будут иметь подсписок, в вашем случае, из одного или двух пунктов. Если ваше имя включает расширение .txt или .mp3, возможно, вам придется написать производное свойство, чтобы удалить его и сгруппировать по нему.

Brian Parker 21.11.2022 15:38

@BrianParker Я наткнулся на System.IO.Path.ChangeExtension(path, null); Который позволил бы мне отрезать расширения, сохранив путь, но это не сработало. Вы можете проверить редактирование в моем посте и посмотреть, что я сделал неправильно?

Addlon 21.11.2022 16:07
[JS за 1 час] - 9. Асинхронный
[JS за 1 час] - 9. Асинхронный
JavaScript является однопоточным, то есть он может обрабатывать только одну задачу за раз. Для обработки длительных задач, таких как сетевые запросы,...
Топ-10 компаний-разработчиков PHP
Топ-10 компаний-разработчиков PHP
Если вы ищете надежных разработчиков PHP рядом с вами, вот список лучших компаний по разработке PHP.
Скраппинг поиска Apple App Store с помощью Python
Скраппинг поиска Apple App Store с помощью Python
📌Примечание: В этой статье я покажу вам, как скрапировать поиск Apple App Store и получить точно такой же результат, как на Apple iMac, потому что...
Редкие достижения на Github ✨
Редкие достижения на Github ✨
Редкая коллекция доступна в профиле на GitHub ✨
Подъем в javascript
Подъем в javascript
Hoisting - это поведение в JavaScript, при котором переменные и объявления функций автоматически "перемещаются" в верхнюю часть соответствующих...
Улучшение генерации файлов Angular
Улучшение генерации файлов Angular
Angular - это фреймворк. Вы можете создать практически любое приложение без использования сторонних библиотек.
0
4
103
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Используйте Linq GroupBy

@foreach(var fileGroup in myFilesGroupedAndSorted)
{
    <h3>@fileGroup.Key</h3>
    @foreach(var file in fileGroup.OrderBy( file => file.Path))
    {
        <div>@file.Path</div>
    }
}

@code {
    // without access to your folder I generated random data.
    List<TextFile> myList = new List<TextFile>
    {
        new TextFile { Name = "", Path = "some-path\\SomeFile1.txt", Url = "" },
        new TextFile { Name = "", Path = "some-path\\SomeFile2.txt", Url = "" },
        new TextFile { Name = "", Path = "some-path\\SomeFile3.txt", Url = "" },
        new TextFile { Name = "", Path = "some-path\\SomeFile4.txt", Url = "" },
        new TextFile { Name = "", Path = "some-path\\SomeFile5.txt", Url = "" },
        new TextFile { Name = "", Path = "some-path\\SomeFile6.txt", Url = "" },
        new TextFile { Name = "", Path = "some-path\\SomeFile1.mp3", Url = "" },
        new TextFile { Name = "", Path = "some-path\\SomeFile2.mp3", Url = "" },
        new TextFile { Name = "", Path = "some-path\\SomeFile3.mp3", Url = "" },
        new TextFile { Name = "", Path = "some-path\\SomeFile4.mp3", Url = "" },
        new TextFile { Name = "", Path = "some-path\\SomeFile5.mp3", Url = "" },
        new TextFile { Name = "", Path = "some-path\\SomeFile6.mp3", Url = "" },
    };


    IEnumerable<IGrouping<string, TextFile>> myFilesGroupedAndSorted
        => myList.GroupBy(file => GetPathWithoutExtension(file.Path))
                 .OrderBy(group => group.Key);

    private string GetPathWithoutExtension(string path)
    {
        return System.IO.Path.ChangeExtension(path, null);
    }
}

Затем обработка каждой группы должна быть тривиальной путем тестирования каждого расширения файла. Я бы сделал подкомпонент для передачи IGrouping<string, TextFile> в качестве параметра, чтобы упростить логику...

Mp3.Razor

<tr>
    <td>
        @FileGroup.Key
    </td>
    <td>
        @if (Mp3 is not null)
        {
            <span @onclick = "() => PlayAudio(Mp3.Url)"
              class = "text-primary oi oi-play-circle me-2" aria-hidden = "true" role = "button">
            </span>
            <span @onclick = "() => DeleteAudio(FileGroup.Key)"
              class = "text-danger oi oi-trash" aria-hidden = "true" role = "button">
            </span>
        }
        @if (Text is not null)
        {
            <span @onclick = "() => openTextFile(Text)">
                <button>Open</button>
            </span>
        }
    </td>
</tr>
@code {
    [Parameter]
    public IGrouping<string, TextFile> FileGroup { get; set; } = default!;

    TextFile? Text => FileGroup.FirstOrDefault(file => Path.GetExtension(file.Path).ToLower() == "txt");
    TextFile? Mp3 => FileGroup.FirstOrDefault(file => Path.GetExtension(file.Path).ToLower() == "mp3");
}
...
  <tbody>
    @foreach(var fileGroup in myFilesGroupedAndSorted)
    {
       <Mp3 FileGroup=fileGroup /> 
    }
  </tbody>
...

Я пробовал кое-что, но список так и не появился. Могу я попросить вас проверить редактирование кода в исходном сообщении и посмотреть, что не так?

Addlon 21.11.2022 20:39

@Addlon Во-первых, заполняется ли список должным образом? Можете ли вы поставить точку останова и проверить ее содержимое?

Brian Parker 21.11.2022 21:00

Я получаю исключение System.ArgumentNullException: 'Value cannot be null. (Parameter 'source')' в Text => FileGroup... и Mp3 =>....

Addlon 22.11.2022 11:32

Извините, что беспокою вас, но в таблице теперь перечислены @fileGroup.Key, но только когда TextFile? Text => fileGroup... и TextFile? Mp3 => fileGroup... удалены, а также 2 оператора if, в которых они упоминаются. Когда они там, они получают исключение ArgumentNullException. Вы знаете, почему это происходит?

Addlon 22.11.2022 16:25

@Addlon Ошибка — это фильтр для расширения. Возврат Path.GetExtension включает ., поэтому замените два == "mp3" на = = ".mp3". Это была опечатка от моего имени.

Brian Parker 22.11.2022 17:28

Я могу с сожалением сказать, что ошибка все еще сохраняется с '.' добавлен

Addlon 22.11.2022 17:32

@Addlon github.com/BrianLParker/TwoFilesLinkedQuestionOnStack

Brian Parker 22.11.2022 17:49

У меня есть аудиоэлемент в Mp3Component, который находится за пределами <tr>...</tr>, но вызывается в цикле foreach. Есть ли способ отделить аудиоэлемент в Mp3Component от того, что происходит в цикле, чтобы на него можно было ссылаться вне foreach?

Addlon 23.11.2022 16:33

Вам нужно «повернуть» таблицу, т.е. преобразовать список в таблицу. Для этого можно использовать LINQ. Проверьте этот ответ. Вместо «Jan» и «Feb» у вас будут свойства bool HasMp3 и bool HasTxt с соответствующими пунктами Where. Что-то вроде этого:

var files = new DirectoryInfo(path).GetFiles();

textList.AddRange(files
    .GroupBy(c => Path.ChangeExtension(c.Name, null))
    .Select(g => new TextFile() {
        Name = g.Key,
        Url = $"/textFiles/{g.Key}",
        HasMp3 = g.Any(c => Path.GetExtension(c) == ".mp3"),
        HasTxt = g.Any(c => Path.GetExtension(c) == ".txt"),
    }));

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

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