Преобразуйте массив в индексный хеш в Ruby

Модуль body-parser обрабатывает только сообщения JSON и urlencoded, а не multipart (это было бы так, если вы загружаете файлы).

Для multipart вы 'd нужно использовать что-то вроде connect-busboy или multer или connect-multiparty (многопартийное / грозное - это то, что первоначально использовалось в экспресс-курьерское промежуточное ПО). Кроме того, FWIW, я работаю над еще более высоким уровнем над буксиром под названием reformed . Он поставляется с промежуточным программным обеспечением Express, а также может использоваться отдельно.

42
задан derobert 19 July 2013 в 17:23
поделиться

11 ответов

Если все, для чего Вы нуждаетесь в хеше, является членством, рассмотрите использование Set :

Набор

Набор реализации набор незаказанных значений без дубликатов. Это - гибрид интуитивных средств взаимодействия Массива и быстрого поиска Хеша.

Набор прост в использовании с [1 114] Счетный объекты (реализующий each). Большинство методов инициализатора и бинарных операторов принимают универсальный Счетный объекты помимо наборов и массивов. Счетный объект может быть преобразован в [1 117] Набор использование to_set метод.

Набор Хеш использования как устройство хранения данных, таким образом, необходимо отметить следующие моменты:

  • Равенство элементов определяется согласно Object#eql? и Object#hash.
  • Набор предполагает, что идентификационные данные каждого элемента не изменяются, в то время как это хранится. Изменение элемента набора представит набор к ненадежному состоянию.
  • , Когда строка должна быть сохранена, замороженная копия строки хранится вместо этого, если исходная строка уже не замораживается.

Сравнение

операторы сравнения <, >, <= и >= реализованы как стенография для {надлежащий _} {подмножество?, надмножество?} методы. Однако <=> оператор намеренно не учтен, потому что не каждая пара наборов сопоставима. ({x, y} по сравнению с {x, z}, например)

Пример

require 'set'
s1 = Set.new [1, 2]                   # -> #<Set: {1, 2}>
s2 = [1, 2].to_set                    # -> #<Set: {1, 2}>
s1 == s2                              # -> true
s1.add("foo")                         # -> #<Set: {1, 2, "foo"}>
s1.merge([2, 6])                      # -> #<Set: {1, 2, "foo", 6}>
s1.subset? s2                         # -> false
s2.subset? s1                         # -> true

[...]

Общедоступные Методы класса

новый (перечисление = ноль)

Создает новый набор, содержащий элементы данного счетного объекта.

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

43
ответ дан Ajedi32 26 November 2019 в 23:28
поделиться

Вот аккуратный способ кэшировать поиски с Хешем:

a = (1..1000000).to_a
h = Hash.new{|hash,key| hash[key] = true if a.include? key}

В значительной степени то, что это делает, создают конструктора по умолчанию для новых значений хэш-функции, затем хранит "верный" в кэше, если это находится в массиве (ноль иначе). Это позволяет ленивую загрузку в кэш, на всякий случай Вы не используете каждый элемент.

0
ответ дан zenazn 26 November 2019 в 23:28
поделиться

Выполнение некоторого сравнительного тестирования на предложениях до сих пор дает это, chrismear и основанное на присвоении создание хеша Gaius немного быстрее, чем мой метод карты (и присваивающийся ноль немного быстрее, чем присвоение верного). mtyaka и предложение Набора rampion приблизительно на 35% медленнее для создания.

До поисков, hash.include?(x) очень крошечная сумма быстрее, чем hash[x]; оба дважды как быстрое как set.include?(x).

                user     system      total        real
chrismear   6.050000   0.850000   6.900000 (  6.959355)
derobert    6.010000   1.060000   7.070000 (  7.113237)
Gaius       6.210000   0.810000   7.020000 (  7.049815)
mtyaka      8.750000   1.190000   9.940000 (  9.967548)
rampion     8.700000   1.210000   9.910000 (  9.962281)

                user     system      total        real
times      10.880000   0.000000  10.880000 ( 10.921315)
set        93.030000  17.490000 110.520000 (110.817044)
hash-i     45.820000   8.040000  53.860000 ( 53.981141)
hash-e     47.070000   8.280000  55.350000 ( 55.487760)

код Сравнительного тестирования:

#!/usr/bin/ruby -w
require 'benchmark'
require 'set'

array = (1..5_000_000).to_a

Benchmark.bmbm(10) do |bm|
    bm.report('chrismear') { hash = {}; array.each{|x| hash[x] = nil} }
    bm.report('derobert')  { hash = Hash[array.map {|x| [x, nil]}] }
    bm.report('Gaius')     { hash = {}; array.each{|x| hash[x] = true} }
    bm.report('mtyaka')    { set = array.to_set }
    bm.report('rampion')   { set = Set.new(array) }
end

hash = Hash[array.map {|x| [x, true]}]
set = array.to_set
array = nil
GC.start

GC.disable
Benchmark.bmbm(10) do |bm|
    bm.report('times')  { 100_000_000.times { } }
    bm.report('set')    { 100_000_000.times { set.include?(500_000) } }
    bm.report('hash-i') { 100_000_000.times { hash.include?(500_000) } }
    bm.report('hash-e') { 100_000_000.times { hash[500_000] } }
end
GC.enable
1
ответ дан derobert 26 November 2019 в 23:28
поделиться

Я думаю , точка chrismear при использовании присвоения по созданию является большой. Для создания всего этого немного большим количеством Ruby-esque, тем не менее, я мог бы предложить присвоить что-то другой , чем nil к каждому элементу:

hash = {}
array.each { |x| hash[x] = 1 } # or true or something else "truthy"
...
if hash[376]                   # instead of if hash.has_key?(376)
  ...
end

проблема с присвоением nil состоит в том, что необходимо использовать has_key? вместо [], так как [] дают Вам nil (Ваше значение маркера), если эти Hash не имеет указанного ключа. Вы могли обходить это при помощи различного значения по умолчанию, но почему проходят дополнительную работу?

# much less elegant than above:
hash = Hash.new(42)
array.each { |x| hash[x] = nil }
...
unless hash[376]
  ...
end
2
ответ дан Community 26 November 2019 в 23:28
поделиться

Возможно, я неправильно понимаю цель здесь; Если Вы хотели знать, был ли X в массиве, почему бы не array.include? ("X")?

1
ответ дан capotej 26 November 2019 в 23:28
поделиться

Rampion побеждают меня к нему. Набор мог бы быть ответом.

можно сделать:

require 'set'
set = array.to_set
set.include?(x)
5
ответ дан mtyaka 26 November 2019 в 23:28
поделиться

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

hash = {}
array.each{|x| hash[x] = nil}

Это не выглядит особенно изящным, но это ясно, и делает задание.

FWIW, Ваше исходное предложение (под Ruby 1.8.6, по крайней мере), кажется, не работает. Я получаю "ArgumentError: нечетное число аргументов в пользу Хеша" ошибка. Хеш. [] ожидает литерал, даже-lengthed список значений:

Hash[a, 1, b, 2] # => {a => 1, b => 2}

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

hash = Hash[*array.map {|x| [x, nil]}.flatten]

, но производительность страшно:

#!/usr/bin/ruby -w
require 'benchmark'

array = (1..100_000).to_a

Benchmark.bm(15) do |x|
  x.report("assignment loop") {hash = {}; array.each{|e| hash[e] = nil}}
  x.report("hash constructor") {hash = Hash[*array.map {|e| [e, nil]}.flatten]}
end

дает

                     user     system      total        real
assignment loop  0.440000   0.200000   0.640000 (  0.657287)
hash constructor  4.440000   0.250000   4.690000 (  4.758663)

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

6
ответ дан chrismear 26 November 2019 в 23:28
поделиться

Если Вы хотите быстро спросить, "X в массиве?" необходимо использовать Array#include? .

Редактирование (в ответ на дополнение в OP):

, Если Вы хотите быстрый взгляд времена, используйте Набор. При наличии Хеша, который указывает на весь nil, с глупа. Преобразование является легким процессом также с Array#to_set.

require 'benchmark'
require 'set'

array = (1..1_000_000).to_a
set = array.to_set

Benchmark.bm(15) do |x|
    x.report("Array.include?") { 1000.times { array.include?(500_000) } }
    x.report("Set.include?") { 1000.times { set.include?(500_000) } }
end

Результаты на моей машине:

                     user     system      total        real
Array.include?  36.200000   0.140000  36.340000 ( 36.740605)
Set.include?     0.000000   0.000000   0.000000 (  0.000515)

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

9
ответ дан Zach Langley 26 November 2019 в 23:28
поделиться

Ваш способ создать хеш выглядит хорошим. У меня был навоз вокруг в irb, и это иначе

>> [1,2,3,4].inject(Hash.new) { |h,i| {i => nil}.merge(h) }
=> {1=>nil, 2=>nil, 3=>nil, 4=>nil}
4
ответ дан dylanfm 26 November 2019 в 23:28
поделиться

Если Вы ищете эквивалент этого кода Perl:

grep {$_ eq $element} @array

Можно просто использовать простой код Ruby:

array.include?(element)
0
ответ дан Sophie Alpert 26 November 2019 в 23:28
поделиться

Если вас не беспокоит, что такое хэш-значения

irb(main):031:0> a=(1..1_000_000).to_a ; a.length
=> 1000000
irb(main):032:0> h=Hash[a.zip a] ; h.keys.length
=> 1000000

Занимает секунду или около того на моем рабочем столе.

1
ответ дан 26 November 2019 в 23:28
поделиться
Другие вопросы по тегам:

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