Преобразование списка с дубликатами в словарь в C # [дубликат]

Каждый раз, когда вы получаете ...

"Warning: mysqli_fetch_object () ожидает, что параметр 1 будет mysqli_result, boolean задан«

... это, вероятно, из-за проблемы с вашим запросом. prepare() или query() могут возвращать FALSE (логическое), но это общее сообщение об отказе не оставляет вас в стороне от подсказок. Как вы узнаете, что не так с вашим запросом? Вы задаете !

Прежде всего убедитесь, что сообщения об ошибках включены и видны: добавьте эти две строки в начало файла (ов) сразу после открытия <?php:

error_reporting(E_ALL);
ini_set('display_errors', 1);

Если ваше сообщение об ошибках установлено в php.ini, вам не придется беспокоиться об этом. Просто убедитесь, что вы обрабатываете ошибки изящно и никогда не раскрываете истинные причины каких-либо проблем для ваших пользователей. Выявление истинной причины для общественности может быть приглашением на золото с гравировкой для тех, кто хочет нанести вред вашим сайтам и серверам. Если вы не хотите отправлять ошибки в браузер, вы всегда можете следить за журналами ошибок веб-сервера. Расположение журналов будет варьироваться от сервера к серверу, например, на Ubuntu журнал ошибок обычно находится в /var/log/apache2/error.log. Если вы изучаете журналы ошибок в среде Linux, вы можете использовать tail -f /path/to/log в окне консоли, чтобы видеть ошибки, когда они происходят в режиме реального времени .... или как вы их делаете.

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

$sql = "SELECT `foo` FROM `weird_words` WHERE `definition` = ?";
$query = $mysqli->prepare($sql)); // assuming $mysqli is the connection
$query->bind_param('s', $definition);
$query->execute();

Ошибка является общей и не очень помогает вам в решении того, что происходит.

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

$sql = "SELECT `foo` FROM `weird_words` WHERE `definition` = ?";
if($query = $mysqli->prepare($sql)) { // assuming $mysqli is the connection
    $query->bind_param('s', $definition);
    $query->execute();
    // any additional code you need would go here.
} else {
    $error = $mysqli->errno . ' ' . $mysqli->error;
    echo $error; // 1054 Unknown column 'foo' in 'field list'
}

Если что-то не так, вы можете выплюнуть сообщение об ошибке, которое приведет вас к проблеме , В этом случае в таблице нет столбца foo, решение проблемы тривиально.

Если вы выберете, вы можете включить эту проверку в функцию или класс и расширить ее, обработав ошибки изящно, как упомянутых ранее.

18
задан ekostadinov 7 November 2015 в 09:56
поделиться

10 ответов

Этого не существует, но вы можете быстро построить его из словаря и списка:

class MultiDict<TKey, TValue>  // no (collection) base class
{
   private Dictionary<TKey, List<TValue>> _data =  new Dictionary<TKey,List<TValue>>();

   public void Add(TKey k, TValue v)
   {
      // can be a optimized a little with TryGetValue, this is for clarity
      if (_data.ContainsKey(k))
         _data[k].Add(v)
      else
        _data.Add(k, new List<TValue>() { v}) ;
   }

   // more members
}
10
ответ дан Zzz 20 August 2018 в 21:16
поделиться
  • 1
    В списках нет очень эффективных вставок, не так ли? Разве они не используют массив внутри? – mpen 3 October 2010 в 19:23
  • 2
    Да, они используют массив, и иногда List.Add () нужно будет перераспределить. Но в основном Add - O (1). – Henk Holterman 3 October 2010 в 19:27
  • 3
    @Mark: List<T>.Add имеет амортизированную производительность O (1) и обычно быстрее , чем LinkedList<T>.AddLast, потому что для этого требуется меньше операций. LinkedList<T>.AddLast может иметь смысл, если вы хотите, чтобы never имел изменение размера O (n) (даже если это произойдет редко на List<T>, который вы только добавляете). – Dan Tao 3 October 2010 в 19:31
  • 4
    @Mark: Да. Я редко видел случаи, когда было целесообразно выбрать LinkedList<T> над List<T>, когда вы никогда не вставляете / удаляете из середины (там, где действительно светит LinkedList<T>). Когда вы просто добавляете end , LinkedList<T> просто стоит вам гораздо больше (например, создание LinkedListNode<T> objects = & gt; больше полей для инициализации = & gt; удаление массивов ' преимущество производительности благодаря чрезвычайно оптимизированному доступу к памяти) для очень небольшой выгоды: один LinkedList<T>.AddLast в голубой луне будет превосходить один List<T>.Add. Остальное будет наоборот. – Dan Tao 3 October 2010 в 19:39
  • 5
    Список также больше похож на кеш по сравнению со связанным списком – naiem 6 December 2012 в 15:15

Просто добавьте мои $ 0.02 в коллекцию решений:

У меня была такая же потребность еще в 2011 году и была создана MultiDictionary с педантично полной реализацией всех интерфейсов .NET. Это включает перечисления, которые возвращают стандарт KeyValuePair<K, T> и поддерживают свойство IDictionary<K, T>.Values, предоставляя набор фактических значений (вместо ICollection<ICollection<T>>).

Таким образом, он вписывается аккуратно с остальными классов коллекции .NET. Я также определил интерфейс IMultiDictionary<K, T> для доступа к операциям, которые относятся к этому типу словаря:

public interface IMultiDictionary<TKey, TValue> :
  IDictionary<TKey, ICollection<TValue>>,
  IDictionary,
  ICollection<KeyValuePair<TKey, TValue>>,
  IEnumerable<KeyValuePair<TKey, TValue>>,
  IEnumerable {

  /// <summary>Adds a value into the dictionary</summary>
  /// <param name="key">Key the value will be stored under</param>
  /// <param name="value">Value that will be stored under the key</param>
  void Add(TKey key, TValue value);

  /// <summary>Determines the number of values stored under a key</summary>
  /// <param name="key">Key whose values will be counted</param>
  /// <returns>The number of values stored under the specified key</returns>
  int CountValues(TKey key);

  /// <summary>
  ///   Removes the item with the specified key and value from the dictionary
  /// </summary>
  /// <param name="key">Key of the item that will be removed</param>
  /// <param name="value">Value of the item that will be removed</param>
  /// <returns>True if the item was found and removed</returns>
  bool Remove(TKey key, TValue value);

  /// <summary>Removes all items of a key from the dictionary</summary>
  /// <param name="key">Key of the items that will be removed</param>
  /// <returns>The number of items that have been removed</returns>
  int RemoveKey(TKey key);

}

Его можно скомпилировать на любом из .NET 2.0 вверх и пока я развернул его на Xbox 360, Windows Phone 7, Linux и Unity 3D.

Код лицензируется в соответствии с Common Public License (коротко: все идет, но исправления ошибок в библиотеке код должен быть опубликован) и можно найти в моем репозитории Subversion .

2
ответ дан Cygon 20 August 2018 в 21:16
поделиться

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

У него есть класс «MultiValueDictionary», который наследуется от Словаря.

Он также имеет класс расширения, который позволяет вам использовать специальную функцию добавления в любом словаре, где тип значения - это IList; таким образом вы не будете вынуждены использовать пользовательский класс, если вы этого не хотите.

public class MultiValueDictionary<KeyType, ValueType> : Dictionary<KeyType, List<ValueType>>
{
    /// <summary>
    /// Hide the regular Dictionary Add method
    /// </summary>
    new private void Add(KeyType key, List<ValueType> value)
    {            
        base.Add(key, value);
    }

    /// <summary>
    /// Adds the specified value to the multi value dictionary.
    /// </summary>
    /// <param name="key">The key of the element to add.</param>
    /// <param name="value">The value of the element to add. The value can be null for reference types.</param>
    public void Add(KeyType key, ValueType value)
    {
        //add the value to the dictionary under the key
        MultiValueDictionaryExtensions.Add(this, key, value);
    }
}

public static class MultiValueDictionaryExtensions
{
    /// <summary>
    /// Adds the specified value to the multi value dictionary.
    /// </summary>
    /// <param name="key">The key of the element to add.</param>
    /// <param name="value">The value of the element to add. The value can be null for reference types.</param>
    public static void Add<KeyType, ListType, ValueType>(this Dictionary<KeyType, ListType> thisDictionary, 
                                                         KeyType key, ValueType value)
    where ListType : IList<ValueType>, new()
    {
        //if the dictionary doesn't contain the key, make a new list under the key
        if (!thisDictionary.ContainsKey(key))
        {
            thisDictionary.Add(key, new ListType());
        }

        //add the value to the list at the key index
        thisDictionary[key].Add(value);
    }
}
4
ответ дан Doctor Jones 20 August 2018 в 21:16
поделиться
  • 1
    Конечно ... наследуйте от dict со списком! Умная. Что это за new() бит? Не думайте, что я видел это раньше. – mpen 3 October 2010 в 19:28
  • 2
    Взято из MSDN; Используйте новый модификатор, чтобы явно скрыть элемент, унаследованный от базового класса. Чтобы скрыть унаследованный элемент, объявите его в производном классе с использованием того же имени и измените его с помощью нового модификатора. – Doctor Jones 3 October 2010 в 19:30
  • 3
    @DoctaJonez - я думаю, он спрашивает об ограничении общих параметров (только new() в коде). @Mark - это ограничение общего параметра , требующее, чтобы переданный в родовом типе имел открытый конструктор без параметров. – Oded 3 October 2010 в 19:32
  • 4
    @DoctaJonez: Я думаю, что @Mark означает ваше ограничение where ListType : new(). (@Mark: Это означает, что ListType должен иметь открытый конструктор без параметров. Кроме того, я удивлен, что вам нужен этот ответ, который скрывает метод base.Add, поскольку кажется, что вам не нравится этот аспект ответа Одеда.) – Dan Tao 3 October 2010 в 19:35
  • 5
    @DemetrisLeptos. Другим способом достижения этого было бы явное внедрение интерфейса IDictionary в классе MultiValueDictionary. Затем вы можете выбрать, какие члены интерфейса вы хотите обработать базовым классом, и какие из них вы хотите реализовать самостоятельно (в этом случае «Добавить»). – Doctor Jones 5 June 2017 в 10:46

Вы можете легко сделать это из словаря списков:

public class MultiValueDictionary<Key, Value> : Dictionary<Key, List<Value>> {

  public void Add(Key key, Value value) {
    List<Value> values;
    if (!this.TryGetValue(key, out values)) {
      values = new List<Value>();
      this.Add(key, values);
    }
    values.Add(value);
  }

}
20
ответ дан Guffa 20 August 2018 в 21:16
поделиться

Microsoft просто добавила официальную предварительную версию именно того, что вы ищете (называемый MultiDictionary), доступный через NuGet здесь: https://www.nuget.org/packages/Microsoft.Experimental.Collections/

Информацию об использовании и более подробную информацию можно найти в официальном сообщении в блоге MSDN здесь: http://blogs.msdn.com/b/dotnet/archive/2014/06/ 20 / would-you-like-a-multidictionary.aspx

Я разработчик этого пакета, поэтому дайте мне знать либо здесь, либо в MSDN, если у вас есть какие-либо вопросы о производительности или что-нибудь.

Надеюсь, что это поможет.

33
ответ дан Ian Hays 20 August 2018 в 21:16
поделиться
  • 1
    Этот ответ должен подняться. – i3arnon 21 June 2014 в 03:40
  • 2
    Есть ли опечатка во втором фрагменте? myDictionary.Add(2) - вам не нужно указывать ключ? – mpen 21 June 2014 в 04:44
  • 3
    @Mark, вы правы, во втором примере была ошибка, но с тех пор она была исправлена. Спасибо, что указали это :) – Ian Hays 23 June 2014 в 17:49
  • 4
    Он был переименован в «MultiValueDictionary». – Jason Boyd 23 July 2015 в 19:27
  • 5
    @JasonBoyd Ха. Это потрясающе. Я назвал это 5 лет назад. – mpen 11 August 2015 в 20:56

Вы можете использовать класс MultiDictionary из PowerCollections .

Он возвращает ICollection {TValue} для запрошенного ключа.

2
ответ дан Ivan Akcheurov 20 August 2018 в 21:16
поделиться

Это должно быть сделано до сих пор ...

public class MultiValueDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
    private Dictionary<TKey, LinkedList<TValue>> _dict = new Dictionary<TKey, LinkedList<TValue>>();

    public void Add(TKey key, TValue value)
    {
        if(!_dict.ContainsKey(key)) _dict[key] = new LinkedList<TValue>();
        _dict[key].AddLast(value);
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        foreach (var list in _dict)
            foreach (var value in list.Value)
                yield return new KeyValuePair<TKey, TValue>(list.Key, value);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
0
ответ дан mpen 20 August 2018 в 21:16
поделиться
  • 1
    Почему (неудобный) LinkedList? – Henk Holterman 3 October 2010 в 19:24
  • 2
    Я согласен с этим и с идеями Хенка. Лично мне не нравится идея наследования из Dictionary<TKey, List<TValue>> (или аналогичного), поскольку я думаю, что он слишком разоблачает (например, код вызова может добавлять / удалять из значения List<T>, например). – Dan Tao 3 October 2010 в 19:27
  • 3
    @Henk: Потому что я думал, что это будет более эффективно, чем List (см. Обсуждение под вашим ответом). @ Dan: Да ... Я собирался реализовать IDictionary вместо этого, но даже это ... некоторые из методов не имеют смысла. – mpen 3 October 2010 в 19:58
  • 4
    Да, я видел дискуссию. Я думал так же, простое присутствие LinkedList может немного ввести в заблуждение. Он используется редко. – Henk Holterman 3 October 2010 в 20:16

Вы можете всегда использовать Tuple для вашего второго общего параметра:

var dict = new Dictionary<string,Tuple<string,int,object>>();
dict.Add("key", new Tuple<string,int,object>("string1", 4, new Object()));

Или даже общий список как второй общий параметр:

var dict = new Dictionary<string,List<myType>>();

Это позволит вам привязать несколько значений к одному ключу.

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

4
ответ дан Oded 20 August 2018 в 21:16
поделиться
  • 1
    Да, но тогда я должен проверить, существует ли ключ, прежде чем добавлять к нему каждый раз. Я хочу, чтобы класс коллекции обрабатывал это автоматически. – mpen 3 October 2010 в 19:13
  • 2
    Вы можете наследовать от словаря, переопределяя add, чтобы выполнить эту проверку. Это не ресурсоемкий процесс, так как проверка ключа довольно быстрая. Не оптимизируйте перед тем, как нужно . – Oded 3 October 2010 в 19:15
  • 3
    Никто ничего не сказал об оптимизации, это просто удобство. И если я наследую словарь, я уверен, что мне придется new метод Add, а не override, который я обычно не люблю делать, потому что он нарушает наследование. Кроме того, подписи методов должны быть немного разными, потому что некоторые из них должны фактически возвращать списки TValues, а не только TValue. Наконец, я не совсем уверен, почему вы предложили Tuple .. они не расширяемы? – mpen 3 October 2010 в 19:22
  • 4
    @Mark - Вы говорите «несколько значений для ключа». Не ясно, что ценности должны быть расширяемыми. См. Мое обновление, касающееся второго типа, являющегося List. – Oded 3 October 2010 в 19:24
  • 5
    @Oded: Вот почему я написал абзац ниже;) & quot; он просто добавит другое значение к этому ключу & quot; re: update: метод расширения - это умное решение ... Я хотел, чтобы перечислитель возвращал KeyValuePairs, но не Key, List & lt; Value & gt; пар. – mpen 3 October 2010 в 19:31

Однако здесь - моя попытка с использованием ILookup<TKey, TElement> и внутренней KeyedCollection. Убедитесь, что свойство key неизменно. Cross здесь .

public class Lookup<TKey, TElement> : Collection<TElement>, ILookup<TKey, TElement>
{
  public Lookup(Func<TElement, TKey> keyForItem)
    : base((IList<TElement>)new Collection(keyForItem))
  {
  }

  new Collection Items => (Collection)base.Items;

  public IEnumerable<TElement> this[TKey key] => Items[key];
  public bool Contains(TKey key) => Items.Contains(key);
  IEnumerator<IGrouping<TKey, TElement>>
    IEnumerable<IGrouping<TKey, TElement>>.GetEnumerator() => Items.GetEnumerator();

  class Collection : KeyedCollection<TKey, Grouping>
  {
    Func<TElement, TKey> KeyForItem { get; }      
    public Collection(Func<TElement, TKey> keyForItem) => KeyForItem = keyForItem;
    protected override TKey GetKeyForItem(Grouping item) => item.Key;

    public void Add(TElement item)
    {
      var key = KeyForItem(item);
      if (Dictionary != null && Dictionary.TryGetValue(key, out var collection))
        collection.Add(item);
      else
        Add(new Grouping(key) { item });
    }

    public bool Remove(TElement item)
    {
      var key = KeyForItem(item);
      if (Dictionary != null && Dictionary.TryGetValue(key, out var collection)
        && collection.Remove(item))
      {
        if (collection.Count == 0)
          Remove(key);
        return true;
      }
      return false;
    }

  }
  class Grouping : Collection<TElement>, IGrouping<TKey, TElement>
  {
    public Grouping(TKey key) => Key = key;
    public TKey Key { get; }
  }
}
1
ответ дан Shimmy 20 August 2018 в 21:16
поделиться

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

public static V getValue<K, V>(this IDictionary<K, V> d, K key) where V : new() {
    V v; if (!d.TryGetValue(key, out v)) { v = new V(); d.Add(key, v); } return v; } 

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

var d = new Dictionary<int, LinkedList<int>>();
d.getValue(1).AddLast(2);
0
ответ дан Slai 20 August 2018 в 21:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: