ما هي أفضل طريقة لتحويل مجموعة تجزئة في روبي
سؤال
في روبي ، بالنظر إلى مجموعة في أحد الأشكال التالية...
[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
حل غير متوازن شقة إدخال يعمل فقط إذا كان غير متوازن قليلا في النهاية.
تأخذ المختفى:
- كلما كان ذلك ممكنا ، إعداد المدخلات إلى هذه الأمور ،
[key, value]
أزواج (a مصفوفة الفرعية لكل عنصر في الخارجي مجموعة). - عندما كنت يمكن أن تفعل ذلك ، إما
#to_h
أوHash::[]
سيتم العمل على حد سواء. - إذا كنت غير قادر على ،
Hash::[]
جنبا إلى جنب مع تنبيه (*
) العمل ، طالما المدخلات متوازنة. - مع غير متوازن و شقة مجموعة المدخلات ، الطريقة الوحيدة هذا العمل في جميع معقول إذا آخر
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}