ما هي أفضل طريقة لتحويل مجموعة تجزئة في روبي

StackOverflow https://stackoverflow.com/questions/39567

  •  09-06-2019
  •  | 
  •  

سؤال

في روبي ، بالنظر إلى مجموعة في أحد الأشكال التالية...

[apple, 1, banana, 2]
[[apple, 1], [banana, 2]]

...ما هي أفضل طريقة لتحويل هذا إلى تجزئة في شكل...

{apple => 1, banana => 2}
هل كانت مفيدة؟

المحلول

ملاحظة:بالنسبة موجزة وفعالة حل الرجاء مراجعة مارك-أندريه Lafortune الجواب أدناه.

هذا الجواب عرضت أصلا كبديل النهج التي تستخدم لشد التي كانت الأكثر شدة upvoted في وقت كتابة هذا التقرير.كان يجب أن أوضح أنني لم أكن أنوي أن أقدم هذا المثال أفضل الممارسات أو نهج فعال.الجواب الأصلية يلي.


تحذير! الحلول باستخدام تتسطح لن تحافظ على مجموعة مفاتيح أو قيم!

بناء على @جون Topley الشعبية الجواب ، دعونا نحاول:

a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]

هذا يلقي خطأ:

ArgumentError: odd number of arguments for Hash
        from (irb):10:in `[]'
        from (irb):10

منشئ كان يتوقع مجموعة من حتى طول (مثلا ، ['k1','v1,'k2','v2']).ما هو أسوأ من ذلك أن مجموعة مختلفة والتي بالارض على طول فقط بصمت تعطينا تجزئة مع قيم غير صحيحة.

إذا كنت ترغب في استخدام مجموعة مفاتيح أو قيم ، يمكنك استخدام خريطة:

h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"

هذا يحافظ على مجموعة رئيسية:

h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}

نصائح أخرى

ببساطة استخدام Hash[*array_variable.flatten]

على سبيل المثال:

a1 = ['apple', 1, 'banana', 2]
h1 = Hash[*a1.flatten(1)]
puts "h1: #{h1.inspect}"

a2 = [['apple', 1], ['banana', 2]]
h2 = Hash[*a2.flatten(1)]
puts "h2: #{h2.inspect}"

باستخدام Array#flatten(1) يحد من التكرار حتى Array مفاتيح وقيم العمل كما هو متوقع.

أفضل طريقة هي استخدام Array#to_h:

[ [:apple,1],[:banana,2] ].to_h  #=> {apple: 1, banana: 2}

علما بأن to_h كما يقبل كتلة:

[:apple, :banana].to_h { |fruit| [fruit, "I like #{fruit}s"] } 
  # => {apple: "I like apples", banana: "I like bananas"}

ملاحظة: to_h يقبل كتلة في روبي 2.6.0+;في أوائل الياقوت يمكنك استخدام بلدي backports جوهرة ، require 'backports/2.6.0/enumerable/to_h'

to_h دون منع عرضه في روبي 2.1.0.

قبل روبي 2.1, يمكن للمرء أن استخدام أقل مقروءة Hash[]:

array = [ [:apple,1],[:banana,2] ]
Hash[ array ]  #= > {:apple => 1, :banana => 2}

أخيرا, أن نكون حذرين من أي حلول باستخدام flatten, هذا يمكن أن تخلق مشاكل مع قيم المصفوفات أنفسهم.

التحديث

روبي 2.1.0 صدر اليوم.وانا يأتي مع Array#to_h (ملاحظات الإصدار و روبي-doc) الذي يحل مسألة تحويل Array إلى Hash.

روبي مستندات سبيل المثال:

[[:foo, :bar], [1, 2]].to_h    # => {:foo => :bar, 1 => 2}

تحرير:رأيت الردود التي نشرت في حين كنت أكتب ، تجزئة[a.لشد] يبدو وسيلة للذهاب.يجب أن يكون غاب قليلا في الوثائق عندما كنت أفكر من خلال الاستجابة.يعتقد الحلول التي كتبت يمكن استخدامها كبدائل إذا لزم الأمر.

النموذج الثاني هو أبسط:

a = [[:apple, 1], [:banana, 2]]
h = a.inject({}) { |r, i| r[i.first] = i.last; r }

a = array, h = hash, r = العائد-تجزئة القيمة (واحد تتراكم علينا في) ، = عنصر في مجموعة

أبرع طريقة أستطيع التفكير في القيام النموذج الأول هو شيء من هذا القبيل:

a = [:apple, 1, :banana, 2]
h = {}
a.each_slice(2) { |i| h[i.first] = i.last }

يمكنك أيضا ببساطة تحويل مجموعة 2D إلى تجزئة باستخدام:

1.9.3p362 :005 > a= [[1,2],[3,4]]

 => [[1, 2], [3, 4]]

1.9.3p362 :006 > h = Hash[a]

 => {1=>2, 3=>4} 

إضافة إلى الإجابة ولكن باستخدام مجهول المصفوفات و التأشير:

Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]

أخذ هذا الجواب على حدة ، بدءا من الداخل:

  • "a,b,c,d" هو في الواقع سلسلة.
  • split على الفواصل في صفيف.
  • zip هذا جنبا إلى جنب مع مجموعة التالية.
  • [1,2,3,4] فعلي مجموعة.

المتوسطة النتيجة هي:

[[a,1],[b,2],[c,3],[d,4]]

تتسطح ثم تحول ذلك إلى:

["a",1,"b",2,"c",3,"d",4]

ثم:

*["a",1,"b",2,"c",3,"d",4] unrolls ذلك في "a",1,"b",2,"c",3,"d",4

والتي يمكننا استخدام الحجج إلى Hash[] الطريقة:

Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]

والتي ينتج:

{"a"=>1, "b"=>2, "c"=>3, "d"=>4}

ملخص & TL;DR:

هذا الجواب يأمل أن يكون شاملا الختامية من المعلومات عن إجابات أخرى.

جدا قصيرة الإصدار ، وبالنظر إلى بيانات من السؤال بالإضافة إلى بعض الإضافات:

flat_array   = [  apple, 1,   banana, 2  ] # count=4
nested_array = [ [apple, 1], [banana, 2] ] # count=2 of count=2 k,v arrays
incomplete_f = [  apple, 1,   banana     ] # count=3 - missing last value
incomplete_n = [ [apple, 1], [banana   ] ] # count=2 of either k or k,v arrays


# there's one option for flat_array:
h1  = Hash[*flat_array]                     # => {apple=>1, banana=>2}

# two options for nested_array:
h2a = nested_array.to_h # since ruby 2.1.0    => {apple=>1, banana=>2}
h2b = Hash[nested_array]                    # => {apple=>1, banana=>2}

# ok if *only* the last value is missing:
h3  = Hash[incomplete_f.each_slice(2).to_a] # => {apple=>1, banana=>nil}
# always ok for k without v in nested array:
h4  = Hash[incomplete_n] # or .to_h           => {apple=>1, banana=>nil}

# as one might expect:
h1 == h2a # => true
h1 == h2b # => true
h1 == h3  # => false
h3 == h4  # => true

مناقشة تفاصيل متابعة.


الإعداد:المتغيرات

من أجل إظهار البيانات سوف تستخدم في خط الهجوم ، أنا خلق بعض المتغيرات لتمثيل مختلف الاحتمالات البيانات.أنها تناسب في الفئات التالية:

على أساس ما تم مباشرة في السؤال ، a1 و a2:

(ملاحظة:أفترض أن apple و banana كان من المفترض أن تمثل المتغيرات.كما فعل الآخرون ، سأكون باستخدام سلاسل من هنا بحيث المدخلات والنتائج يمكن أن تتطابق.)

a1 = [  'apple', 1 ,  'banana', 2  ] # flat input
a2 = [ ['apple', 1], ['banana', 2] ] # key/value paired input

متعدد قيمة المفاتيح و/أو القيم ، a3:

في بعض إجابات أخرى ، هناك احتمال آخر قدم (وأنا توسيع هنا) – مفاتيح و/أو قد تكون قيم المصفوفات من تلقاء نفسها:

a3 = [ [ 'apple',                   1   ],
       [ 'banana',                  2   ],
       [ ['orange','seedless'],     3   ],
       [ 'pear',                 [4, 5] ],
     ]

غير متوازن مجموعة ، a4:

لحسن التدبير ، فكرت في إضافة واحدة على الحالة التي يكون فيها قد يكون غير مكتمل الإدخال:

a4 = [ [ 'apple',                   1],
       [ 'banana',                  2],
       [ ['orange','seedless'],     3],
       [ 'durian'                    ], # a spiky fruit pricks us: no value!
     ]

والآن إلى العمل:

بدءا في البداية مسطحة مجموعة ، a1:

وقد اقترح البعض استخدام #to_h (الذي ظهر في روبي 2.1.0, و يمكن backported الإصدارات السابقة).بالنسبة في البداية مسطحة مجموعة, هذا لا يعمل:

a1.to_h   # => TypeError: wrong element type String at 0 (expected array)

باستخدام Hash::[] جنبا إلى جنب مع تنبيه المشغل لا:

Hash[*a1] # => {"apple"=>1, "banana"=>2}

لذلك هذا هو الحل للحالات البسيطة ممثلة a1.

مع مجموعة من مفتاح/قيمة زوج المصفوفات ، a2:

مع مجموعة من [key,value] نوع المصفوفات ، هناك طريقتان للذهاب.

أولا ، Hash::[] لا يزال يعمل (كما فعلت مع *a1):

Hash[a2] # => {"apple"=>1, "banana"=>2}

ثم أيضا #to_h يعمل الآن:

a2.to_h  # => {"apple"=>1, "banana"=>2}

لذا اثنين إجابات سهلة بسيطة متداخلة مجموعة القضية.

وهذا لا يزال صحيحا حتى مع الفرعية صفائف مفاتيح أو قيم ، مع a3:

Hash[a3] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]} 
a3.to_h  # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]}

ولكن الدوريان يكون المسامير (الشاذة الهياكل تعطي مشاكل):

إذا كنا قد حصلت على البيانات المدخلة ليست متوازنة ، سنقوم بتشغيل إلى مشاكل مع #to_h:

a4.to_h  # => ArgumentError: wrong array length at 3 (expected 2, was 1)

ولكن Hash::[] لا يزال يعمل فقط وضع nil كما قيمة durian (أي عنصر الصفيف في a4 ذلك فقط 1-مجموعة قيمة):

Hash[a4] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}

تسطيح - استخدام المتغيرات الجديدة a5 و a6

بعض إجابات أخرى ذكر flatten, مع أو بدون 1 الحجة, لذلك دعونا إنشاء بعض متغيرات جديدة:

a5 = a4.flatten
# => ["apple", 1, "banana", 2,  "orange", "seedless" , 3, "durian"] 
a6 = a4.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian"] 

اخترت لاستخدام a4 قاعدة البيانات بسبب مشكلة التوازن لدينا ، والتي ظهرت مع a4.to_h.أعتقد الاتصال flatten قد يكون أحد الاقتراب من شخص ما قد تستخدم في محاولة لحل تلك التي قد تبدو على ما يلي.

flatten دون حجج (a5):

Hash[*a5]       # => {"apple"=>1, "banana"=>2, "orange"=>"seedless", 3=>"durian"}
# (This is the same as calling `Hash[*a4.flatten]`.)

في السذاجة وهلة, يبدو ان هذا العمل – ولكن انها حصلت الولايات المتحدة على سفح خاطئ مع وجود بذور البرتقال ، مما جعل أيضا 3 a الرئيسية و durian a القيمة.

هذا مع a1, فقط لا يعمل:

a5.to_h # => TypeError: wrong element type String at 0 (expected array)

لذلك a4.flatten ليست مفيدة لنا كنا نريد فقط أن استخدام Hash[a4]

على flatten(1) الحالة (a6):

ولكن ماذا عن جزئيا فقط تسطيح?ومن الجدير بالذكر أن الدعوة Hash::[] باستخدام splat على جزئيا بالارض مجموعة (a6) هو لا نفس الدعوة Hash[a4]:

Hash[*a6] # => ArgumentError: odd number of arguments for Hash

قبل بالارض مجموعة, لا تزال متداخلة (طريقة بديلة للحصول على a6):

ولكن ماذا إذا كان هذا هو كيف ونحن قد حصلت على مجموعة في المقام الأول ؟ (وهذا هو ، نسبيا إلى a1, كان لدينا إدخال البيانات - فقط هذه المرة بعض البيانات يمكن صفائف أو كائنات أخرى.) لقد رأينا أن Hash[*a6] لا يعمل ، ولكن ماذا إذا كنا لا يزال يرغب في الحصول على السلوك حيث آخر عنصر (مهم!انظر أدناه) بمثابة مفتاح nil قيمة ؟

في مثل هذه الحالة, لا يزال هناك وسيلة للقيام بذلك ، باستخدام Enumerable#each_slice للحصول على أنفسنا مرة أخرى إلى مفتاح/قيمة أزواج كما عناصر الخارجي مجموعة:

a7 = a6.each_slice(2).to_a
# => [["apple", 1], ["banana", 2], [["orange", "seedless"], 3], ["durian"]] 

لاحظ أن هذا ينتهي الحصول على لنا مجموعة جديدة ليس "متطابقة"إلى a4, لكن لا يكون نفس القيم:

a4.equal?(a7) # => false
a4 == a7      # => true

وبالتالي فإننا يمكن أن تستخدم مرة أخرى Hash::[]:

Hash[a7] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}
# or Hash[a6.each_slice(2).to_a]

لكن هناك مشكلة!

من المهم أن نلاحظ أن each_slice(2) الحل الوحيد الأمور في العودة إلى التعقل إذا آخر مفتاح واحد في عداد المفقودين قيمة.إذا كنا وأضاف في وقت لاحق إضافية مفتاح/قيمة الزوج:

a4_plus = a4.dup # just to have a new-but-related variable name
a4_plus.push(['lychee', 4])
# => [["apple",                1],
#     ["banana",               2],
#     [["orange", "seedless"], 3], # multi-value key
#     ["durian"],              # missing value
#     ["lychee", 4]]           # new well-formed item

a6_plus = a4_plus.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian", "lychee", 4]

a7_plus = a6_plus.each_slice(2).to_a
# => [["apple",                1],
#     ["banana",               2],
#     [["orange", "seedless"], 3], # so far so good
#     ["durian",               "lychee"], # oops! key became value!
#     [4]]                     # and we still have a key without a value

a4_plus == a7_plus # => false, unlike a4 == a7

واثنين من التجزئات التي يحصل عليها من هذه تختلف في طرق هامة:

ap Hash[a4_plus] # prints:
{
                     "apple" => 1,
                    "banana" => 2,
    [ "orange", "seedless" ] => 3,
                    "durian" => nil, # correct
                    "lychee" => 4    # correct
}

ap Hash[a7_plus] # prints:
{
                     "apple" => 1,
                    "banana" => 2,
    [ "orange", "seedless" ] => 3,
                    "durian" => "lychee", # incorrect
                           4 => nil       # incorrect
}

(ملاحظة:أنا باستخدام awesome_print's ap فقط لجعله أسهل أن تظهر هيكل هنا ؛ لا يوجد المفاهيمي شرط هذا.)

لذلك each_slice حل غير متوازن شقة إدخال يعمل فقط إذا كان غير متوازن قليلا في النهاية.


تأخذ المختفى:

  1. كلما كان ذلك ممكنا ، إعداد المدخلات إلى هذه الأمور ، [key, value] أزواج (a مصفوفة الفرعية لكل عنصر في الخارجي مجموعة).
  2. عندما كنت يمكن أن تفعل ذلك ، إما #to_h أو Hash::[] سيتم العمل على حد سواء.
  3. إذا كنت غير قادر على ، Hash::[] جنبا إلى جنب مع تنبيه (*) العمل ، طالما المدخلات متوازنة.
  4. مع غير متوازن و شقة مجموعة المدخلات ، الطريقة الوحيدة هذا العمل في جميع معقول إذا آخر value البند الوحيد الذي هو في عداد المفقودين.

الجانب ملاحظة:أنا نشر هذا الجواب لأنني أشعر أن هناك قيمة مضافة – بعض من القائمة إجابات لديهم معلومات غير صحيحة و لا شيء (التي قرأت) كما أعطى كاملة إجابة وأنا تسعى إلى القيام به هنا.وآمل أنه من المفيد.ومع ذلك نشكر أولئك الذين جاؤوا قبل عدة منهم توفير الإلهام أجزاء من هذا الجواب.

إذا كان لديك مجموعة يشبه هذا -

data = [["foo",1,2,3,4],["bar",1,2],["foobar",1,"*",3,5,:foo]]

وتريد العناصر الأولى من كل مجموعة أن تصبح مفاتيح تجزئة بقية العناصر أصبحت قيمة المصفوفات ، ثم يمكنك أن تفعل شيئا من هذا القبيل -

data_hash = Hash[data.map { |key| [key.shift, key] }]

#=>{"foo"=>[1, 2, 3, 4], "bar"=>[1, 2], "foobar"=>[1, "*", 3, 5, :foo]}

لست متأكدا إذا كان هذا هو أفضل وسيلة ، ولكن هذا يعمل:

a = ["apple", 1, "banana", 2]
m1 = {}
for x in (a.length / 2).times
  m1[a[x*2]] = a[x*2 + 1]
end

b = [["apple", 1], ["banana", 2]]
m2 = {}
for x,y in b
  m2[x] = y
end

إذا كانت القيم الرقمية هي يليها فهارس, ثم أننا يمكن أن يكون أبسط الطرق...هنا هو بلدي رمز تقديم, يا روبي قليلا صدئ

   input = ["cat", 1, "dog", 2, "wombat", 3]
   hash = Hash.new
   input.each_with_index {|item, index|
     if (index%2 == 0) hash[item] = input[index+1]
   }
   hash   #=> {"cat"=>1, "wombat"=>3, "dog"=>2}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top