Какие структуры данных я должен использовать для сопоставления трех «старых» значений трем «новым» значениям?

#c# #data-structures #mapping

#c# #структуры данных #сопоставление

Вопрос:

Я пытаюсь выяснить, какая структура данных имела бы смысл для представления моих данных. Я работаю над инструментом C #, который обновит названия определенного набора категорий, подкатегорий и подсубкатегорий. Вот несколько примеров данных:

 OldCategory | OldSubCategory | OldSubSubCategory | NewCategory           | NewSubCategory | NewSubSubCategory
-------------------------------------------------------------------------------------------------------------
Hardware    | Display        | Broken            | HD-Hardware           | Display        |
Hardware    | Display        | Request upgrade   | Requests              | Hardware       | Display 
Software    | Excel          | Error message     | HD-Software           | Excel          | General Error
Software    | Excel          | How Do I          | HD-Software           | Excel          | Training
Software    | Excel          | Plug-in           | HD-SoftwareExtensions | Excel          |
  

Как вы можете видеть, я не могу просто обновить каждое OldCategory до соответствующего NewCategory . Данные построены таким образом, что каждая комбинация Category , SubCategory и SubSubCategory является уникальным значением. Например, Hardware | Display | Request upgrade будет сопоставлено с Requests | Hardware | Display .

Как мне сопоставить комбинацию трех старых значений с комбинацией трех новых значений?

Я рассматривал возможность использования Dictionary<Tuple<string, string, string>, Tuple<string, string, string>> структуры, но это кажется ужасно подробным и потенциально сбивающим с толку других разработчиков или меня в будущем. Есть предложения по более четкому представлению моих данных?

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

1. Почему бы просто не создать класс и не использовать объекты?

2. Можете ли вы добавить пример преобразования?

3. @Bedir Я мог бы. Как вы думаете, имеет ли это смысл в контексте моего вопроса и по сравнению с Dictionary<Tuple<string, string, string>, Tuple<string, string, string>> ?

4. @stuartd Это для нашей системы оформления заявок в службу поддержки. Я взаимодействую с API, и категории являются свойствами заявки. Итак, билет с тремя «старыми» значениями категории должен быть изменен, чтобы иметь три «новых» значения категории. Я надеюсь, что это имеет смысл.

5. Использование Dictionary<3-tuple,3-tuple> просто кажется мне неправильным. Я подозреваю, что использование более детализированной версии ответа @Bedir и Dictionary<Category, Category> было бы проще для понимания, отладки и мониторинга.

Ответ №1:

Вот пример того, о чем я говорил в комментариях:

 public class Category : IEquatable<Category>
{
    public string MainCategory { get; set; }
    public string SubCategory { get; set; }
    public string SubSubCategory { get; set; }


    private string GetCharacterizer()
    {
        return $"{MainCategory}#{SubCategory}#{SubSubCategory}";
    }

    public override int GetHashCode()
    {
        return GetCharacterizer().GetHashCode();
    }

    public bool Equals(Category other)
    {
        if (other == null)
        {
            return false;
        }
        return GetCharacterizer().Equals(other.GetCharacterizer());
    }

    public override bool Equals(object other)
    {
        if (!(other is Category catOther))
        {
            return false;
        }

        return Equals(catOther);
    }
}
  

Затем вы бы создали Dictionary<Category, Category>

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

1. Потрясающе, спасибо. Это более полный ответ, поэтому я отмечу это как ответ.

2. Не обращайте внимания на мой последний (теперь удаленный) комментарий. Я был идиотом 🙂

3. @LewsTherin: Возможно, вы также захотите реализовать операторы == и != (вы должны выполнить оба, если вы выполняете одно). Но, поскольку у вас есть Equals , это просто. Кроме того, напишите несколько тестов, это быстро скомпонованный код

Ответ №2:

Я бы предложил пойти тем же путем, который вы предполагаете, т. Е.

 Dictionary<Tuple<string, string, string>, Tuple<string, string, string>>
  

С новым ValueTuple для этого можно использовать более краткий синтаксис

 var tbl = new Dictionary<(string, string, string), (string, string, string)>();
var newVal = tbl[("Hardware", "Display", "Request upgrade")];
  

Вы также можете использовать имена для полей кортежа, как показано ниже

 var tbl = new Dictionary<(string Cat, string SubCat, string SubSubCat), (string Cat, string SubCat, string SubSubCat)>();
  

Если вам нужно больше гибкости, контроля или возможностей, вы можете использовать класс для этого, как показано ниже

 class CatInfo : Tuple<string, string, string> {
    public string Category => Item1;
    public string SubCategory => Item2;
    public string SubSubCategory => Item3;

    public CatInfo(string cat, string subCat, string subSubCat) : base(cat, subCat, subSubCat) { }
}
  

Если по какой-либо причине вы не можете создать подкласс из Tuple или хотите немного повысить производительность, тогда вы можете просто создать неизменяемую структуру, как показано ниже

 struct CatInfo {
    public string Category { get; }
    public string SubCategory { get; } 
    public string SubSubCatergory { get; }

    public CatInfo(string cat, string subCat, string subSubCat) {
        Category = cat;
        SubCategory = subCat;
        SubSubCatergory = subSubCat;
    }

    public bool Equals(CatInfo other) {
        return string.Equals(Category, other.Category) amp;amp; string.Equals(SubCategory, other.SubCategory) amp;amp; string.Equals(SubSubCatergory, other.SubSubCatergory);
    }

    public override bool Equals(object obj) {
        if (obj is null)
            return false;
        return obj is CatInfo info amp;amp; Equals(info);
    }

    public override int GetHashCode() {
        unchecked {
            int hashCode = (Category != null ? Category.GetHashCode() : 0);
            hashCode = (hashCode * 397) ^ (SubCategory != null ? SubCategory.GetHashCode() : 0);
            hashCode = (hashCode * 397) ^ (SubSubCatergory != null ? SubSubCatergory.GetHashCode() : 0);
            return hashCode;
        }
    }

    public static bool operator ==(CatInfo info1, CatInfo info2) {
        return info1.Equals(info2);
    }

    public static bool operator !=(CatInfo info1, CatInfo info2) {
        return !(info1 == info2);
    }
}
  

Ответ №3:

Создание класса кажется мне лучшим вариантом. Таким образом, вы можете создать конструктор и иметь методы. Например.

 public CategoryClass{
    string category;
    string subCategory;
    string subSubCategory;
}
  

А затем вы можете поместить их в словарь:

 static void Main(string[] args)
{
    CategoryClass oldCategory = new CategoryClass();
    CategoryClass newCategory = new CategoryClass();
    Dictionary<CategoryClass, CategoryClass> dict = new Dictionary<CategoryClass, CategoryClass>();
}
  

… или другой объект.

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

1. Если вы собираетесь это сделать, вам захочется реализовать GetHashKey в классе (например, путем хэширования некоторой конкатенации строк). Вы захотите использовать это как ключ словаря.

2. Я думаю, что именно так я и собираюсь поступить (обернуть категории в класс, а затем настроить сопоставление с помощью Dictionary<ClassName, ClassName> ). Это кажется намного более читаемым. Спасибо!

3. И не забывайте, GetHashKey и другие требования, чтобы ссылочный тип действовал как значение (я не помню их все, но равно и т.д.)

4. @Flydog57 Можете ли вы привести пример того, как бы вы это сделали? Я могу только найти, Object.GetHashCode() и это возвращает int .

5. blogs.msdn.microsoft.com/ericlippert/2011/02/28 / … для GetHashCode. Самый простой способ сделать это — создать закрытую функцию, которая, например, объединяет три строки с разделителем и использует его для равенства, чтобы получить хэш-код (просто возьмите хэш-код объединенных строк) и т.д. И я нашел то, что искал: learn.microsoft.com/en-us/dotnet/csharp/programming-guide /…