Удалить запись, совпав со значением

У меня есть словарь, в котором значения хранятся в следующем формате:

userID, empDetails

Например,

1234, 'empName,jobDesc,CardNumber,Type'

Я должен сравнить эту информацию с другим набором информации таким образом, что -

  • Если введенный userId присутствует в вышеуказанном словаре, то удалите эту запись из словаря.
  • Если введенный CardNumber присутствует (здесь userId неизвестен) в вышеуказанном словаре, то удаляем эту запись из словаря.

Первое условие простое и может быть выполнено

dictionary.Remove(key)

Но я не понимаю, как реализовать второе условие. я хочу что-то вроде

if (CardNumber.PresentinAboveDictionary)
then
Remove that record

Я знаю, что мы можем сравнить неполную строку в таком ключе, но я хочу удалить запись. Проверить, содержит ли какая-либо часть значения хеш-таблицы определенную строку c#

в словаре большое количество записей?

Nitin Sawant 22.12.2022 15:22

@NitinSawant их будет около тысячи.

Anonymous 22.12.2022 15:27

лучше использовать другую эффективную структуру данных.

Nitin Sawant 22.12.2022 15:32

Вас беспокоит производительность или вам просто нужно работающее решение?

SBFrancies 22.12.2022 16:58

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

Anonymous 22.12.2022 17:02
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
100
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

вы можете сделать что-то вроде этого.

var recordsToRemove = dictionary.Where(x => x.Value.Contains("what you are looking for"))
                                .ToList();

        if (recordsToRemove.Any())
        {
            foreach (var record in recordsToRemove)
            {
                dictionary.Remove(record.Key);
            }
        }
Ответ принят как подходящий

Предполагая, что сведения о занятости в вашем словаре представляют собой строку в указанном формате, вам необходимо:

  1. Поиск значений в словаре
  2. Разобрать/разделить значения, чтобы получить номера карт
  3. Проверьте номера карт, чтобы убедиться, что они совпадают с номером карты, которую вы проверяете.
  4. Возвращает пару ключ-значение при совпадении
  5. Удалить запись для ключа в возвращенной паре значений ключа.

Пример кода решения:

var dictionary = new Dictionary<int, string>() { { 1, "empName,jobDesc,124124134,Type" } };
                
var cardNumber = 124124134;
var entry = dictionary.FirstOrDefault(x => DoEmploymentDetailsContainCardNumber(x.Value, cardNumber));

if (!entry.Equals(default(KeyValuePair<int, string>)))
{
    dictionary.Remove(entry.Key);
}

Метод, который проверяет, присутствует ли номер карты в реквизитах занятости:

private static bool DoEmploymentDetailsContainCardNumber(string empDetails, int cardNumber)
{
    var splitEmpDetails = empDetails.Split(',');
    var empDetailsCardNumber = splitEmpDetails[2];
    return empDetailsCardNumber == cardNumber.ToString();
}

вы повторяете все элементы и применяете разделение ко всем, а затем сравниваете, что занимает больше времени, если есть тысячи элементов

Nitin Sawant 22.12.2022 16:15

Дольше чего?

YungDeiza 22.12.2022 16:25

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

SBFrancies 22.12.2022 19:31

@SBFrancies Это подразумевается языком, использованным в вопросе, иначе грамматика неверна.

YungDeiza 22.12.2022 20:15

Вместо Dictionary вы можете использовать строго типизированный List

  1. Используйте встроенный метод Linq Remove
  2. Используйте Parallel.ForEach, повторите список и удалите элемент (осторожно, это займет больше времени)

псевдокод:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using System.Collections;

namespace ConsoleApp4
{
    public class Employee
    {
        public Employee(int userID, string empDetails)
        {
            string[] props = empDetails.Split(new char[] { ',' }, StringSplitOptions.None);
            this.userID = userID;
            this.empName = props[0];
            this.jobDesc = props[1];
            this.CardNumber = props[2];
            this.Type = props[3];
        }

        public int userID { get; set; }
        public string empName { get; set; }
        public string jobDesc { get; set; }
        public string CardNumber { get; set; }
        public string Type { get; set; }
    }

    public class MyCustomList : List<Employee>
    {
        public void Add(int userID, string empDetails)
        {
            this.Add(new Employee(userID, empDetails));
        }

        public bool Remove(string CardNumber)
        {
            bool found = false ;
            Parallel.ForEach(this,
            (i, state) =>
            {
                if (i.CardNumber == CardNumber)
                {
                    this.Remove(i);
                    state.Break();
                }
            });
            return found;
        }

        public bool RemoveV2(string CardNumber)
        {
            bool found = false;
            if (this.Any(x => x.CardNumber == CardNumber))
            {
                this.Remove(this.Where(x => x.CardNumber == CardNumber).First());
                found = true;
            }
            return found;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var dict = new MyCustomList();//userID, empDetails list
            dict.Add(12341, "empName1,jobDesc,CardNumber1,Type");
            dict.Add(12342, "empName2,jobDesc,CardNumber2,Type");
            dict.Add(12343, "empName3,jobDesc,CardNumber3,Type");
            dict.Add(12344, "empName4,jobDesc,CardNumber4,Type");
            dict.Add(12345, "empName5,jobDesc,CardNumber5,Type");
            dict.Add(12346, "empName6,jobDesc,CardNumber6,Type");
            dict.Add(12347, "empName7,jobDesc,CardNumber7,Type");
            dict.Add(12348, "empName8,jobDesc,CardNumber8,Type");

            //remove CardNumber5
            dict.Remove("CardNumber5");
            Console.Write(dict);
        }
    }
}

вы можете следовать простому подходу, чтобы удалить ключ, используя здесь цикл.

Здесь я предполагаю, что в словаре нет ключа со значением -1.

int keyToRemove = -1;
foreach (var entry in dictionary)
{
    if (entry.Value.Contains(CardNumber))
    {
        keyToRemove = entry.Key;
        break;
    }
}

if (keyToRemove != -1)
{
    dictionary.Remove(keyToRemove);
}

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

  1. Сгенерировано 1 000 000 строк данных с уникальными идентификаторами и номерами карт (решение сработало бы и в том случае, если бы номера карт не были уникальными)

  2. Случайным образом удалено 100 000 элементов данных по идентификатору и 100 000 элементов данных по номеру карты.

  3. Сгенерирован список оставшихся элементов данных

Процесс занял около 75 секунд.

Затем я попытался повторить шаги 1) и 2), используя принятый ответ - примерно через 10 минут это около 7% пути удаления элементов данных. Поэтому я думаю, что приведенное ниже решение примерно на 2 порядка быстрее для этого типа операций.

Вероятно, существуют лучшие реализации двусвязных списков, но я не слишком хорошо знаком ни с одним из них.

namespace Question
{
    public class EmployeeCollection 
    {
        private readonly Dictionary<int, ListNode<EmployeeDetails>> _idDictionary = new();
        private readonly Dictionary<string, Dictionary<int, EmployeeDetails>> _cardNumberDictionary = new();
        private readonly LinkedList<EmployeeDetails> _list = new();

        public void AddEmployee(EmployeeDetails details)
        {
            var node = new ListNode<EmployeeDetails>(details);
            _list.AddToStart(node);
            _idDictionary.Add(details.Id, node);
            
            if (!_cardNumberDictionary.ContainsKey(details.CardNumber))
            {
                _cardNumberDictionary.Add(details.CardNumber, new Dictionary<int, EmployeeDetails>());
            }

            _cardNumberDictionary[details.CardNumber].Add(details.Id, details);
        }

        public void RemoveById(int id)
        {
            if (_idDictionary.TryGetValue(id, out var node))
            {
                _idDictionary.Remove(id);
                _list.Remove(node);
                var list = _cardNumberDictionary[node.Value.CardNumber];
                list.Remove(id);

                if (list.Count == 0)
                {
                    _cardNumberDictionary.Remove(node.Value.CardNumber);
                }
            }
        }

        public void RemoveByCardNumber(string cardNumber)
        {
            if (_cardNumberDictionary.TryGetValue(cardNumber, out var employees))
            {
                _cardNumberDictionary.Remove(cardNumber);

                foreach (var employee in employees)
                {
                    if (_idDictionary.TryGetValue(employee.Key, out var node))
                    {
                        _list.Remove(node);
                    }
                }
            }
        }

        public IEnumerable<EmployeeDetails> Employees => _list.GetAllValues();

        public EmployeeDetails? GetById(int id)
        {
            if (_idDictionary.ContainsKey(id))
            {
                return _idDictionary[id].Value;
            }

            return null;
        }
    }

    public class EmployeeDetails
    {
        public int Id { get; init; }
        public string Name { get; init; }
        public string JobDescription { get; init; }
        public string CardNumber { get; init; }
        public string Type { get; init; }

        public static EmployeeDetails FromData(int id, string details)
        {
            var parts = details.Split(',');

            return new EmployeeDetails
            {
                Id = id,
                Name = parts[0],
                JobDescription = parts[1],
                CardNumber = parts[2],
                Type = parts[3],
            };
        }
    }

    public class LinkedList<T>
    {
        public int Count { get; private set; }
        private ListNode<T>? Start { get; set; }
        private ListNode<T>? End { get; set; }
        public bool IsEmpty => Count == 0;

        public void AddToStart(ListNode<T> node)
        {
            ArgumentNullException.ThrowIfNull(nameof(node));
            node.Next = null;
            node.Previous = null;

            if (IsEmpty)
            {
                Start = End = node;
            }
            else
            {
                Start!.Previous = node;
                node.Next = Start;
                Start = node;
            }

            Count++;
        }

        public void Remove(ListNode<T> node)
        {
            if (node != Start)
            {
                node.Previous!.Next = node.Next;
            }

            else
            {
                Start = node.Next;
            }

            if (node != End)
            {
                node.Next!.Previous = node.Previous;
            }

            else
            {
                End = node.Previous;
            }

            Count--;
        }

        public IEnumerable<T> GetAllValues()
        {
            var counter = Start;

            while (counter != null)
            {
                yield return counter.Value;
                counter = counter.Next;
            }
        }
    }

    public class ListNode<T>
    {
        public T Value { get; }
        public ListNode<T>? Previous { get; set; }
        public ListNode<T>? Next { get; set; }

        public ListNode(T value)
        {
            Value = value;
        }
    }
}

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