2015-08-23 5 views
1

У меня есть хэш следующим образом:Сортировка массивов в параллельных

{ 
    :student_name => ['A', 'B', 'C'], 
    :total_marks => [100, 25, 50] 
} 

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

{ 
    :student_name => ['B', 'C', 'A'], 
    :total_marks => [25, 50, 100] 
} 

Что такое масштабируемое решение? Этот хэш может иметь много других атрибутов.

+0

Ваш ожидаемый хэш недействителен. – sawa

+1

Одно из предложений, которое я хотел бы сделать, - это объединить ваши данные, а не как два параллельных массива. В противном случае вам может быть трудно достичь требуемой масштабируемости, потому что одна процедура должна сначала отсортировать массив ': total_marks' и выполнить тот же набор шагов в массиве': student_name'. См. 'Struct' в рубиновых документах о том, как это сделать. –

+1

Вы должны удалить тег Rails. –

ответ

4
h = {:student_name=>["A", "B", "C"], :total_marks=>[100, 25, 50]} 

indices = h[:total_marks].to_enum.with_index.sort_by(&:first).map(&:last) 
h.each{|_, v| v.replace(v.values_at(*indices))} 
#=> {:student_name=>["B", "C", "A"], :total_marks=>[25, 50, 100]} 
+1

Возможно, самое простое решение вопроса OP. Но, чтобы проиллюстрировать мой комментарий к исходному сообщению, это решение требует создания значения, кортежа original_index, сортировки по значению, сохранения (теперь повторно отсортированного) original_indexes и использования индексов для повторной сортировки массива оценки. EDIT: если бы у OP только была правильно отформатированная структура данных, в первую очередь нам не понадобились бы накладные расходы (это то, что я делаю) –

1

Учитывая дизайн вашего хэша, нам нужно сначала соединить ученика и отметки.

h = { 
    :student_name => ['A', 'B', 'C'], 
    :total_marks => [100, 25, 50] 
} 

student_array = h[:student_name] 
marks_array = h[:total_marks] 

student_marks = [] 
student_array.each_with_index {|student, idx| student_marks << [student, marks_array[idx]]} 
p student_marks # Prints [["A", 100], ["B", 25], ["C", 50]] 

Итак, теперь у нас есть массив, где парной студент и знаки, мы сортируем выше массив, используя второго элемент подрешетки

sorted = student_marks.sort {|i, j| i[1] <=> j[1]} 
p sorted # Prints [["B", 25], ["C", 50], ["A", 100]] 

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

h[:student_name] = sorted.collect {|i| i[0]} 
h[:total_marks]= sorted.collect {|i| i[1]} 
p h 

Наконец, мы имеем результат

{:student_name=>["B", "C", "A"], :total_marks=>[25, 50, 100]} 

PS: Посмотрите на sawa's answer на компактный & элегантный способ решения этой проблемы.

5

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

ExamResult = Stuct.new(:student_name, :total_mark) 

exam_results = [ 
    ExamResult.new('Adam', '25'), 
    ExamResult.new('Sajani', '100'), 
    ExamResult.new('Sawa', '50') 
] 

# Sort 
exam_results.sort_by &:total_mark 

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

+0

У моего кода могут быть дополнительные поля без перезаписи. Хотя, я согласен с вами в том, что ОП имеет плохую структуру. – sawa

+1

Если OP должен обрабатывать миллионы, то использование базы данных и обработка сортировки внутри базы данных были бы уместными. Нет ничего более забавного, чем просмотр кода, потраченного на загрузку миллионов записей, а затем сбой. Кроме того, нет необходимости говорить «EDIT» и «EDIT2». Мы можем сказать, что было отредактировано и когда оно произошло. Вместо этого измените документ по мере необходимости, чтобы поддерживать сплоченный и понятный контент. –

+1

'sort_by' не просто чище, это, как правило, намного быстрее. 'sort' будет многократно вычислять промежуточные значения, используемые для сравнения, тогда как' sort_by' запомнит их. Бывают случаи, когда 'sort' приводит к более быстрому алгоритму сортировки в зависимости от того, что сортируется, поэтому бенчмаркинг важен. –

0
marks, names = [h[:total_marks], h[:student_name]].transpose. 
    sort_by(&:first).transpose 
    #=> [[25, 50, 100], ["B", "C", "A"]] 
{ student_name: names, total_marks: marks } 
    #=> {:student_name=>["B", "C", "A"], :total_marks=>[25, 50, 100]} 

шаги:

a = [h[:total_marks], h[:student_name]] 
    #=> [[100, 25, 50], ["A", "B", "C"]] 
b = a.transpose 
    #=> [[100, "A"], [25, "B"], [50, "C"]] 
c = b.sort_by(&:first) 
    #=> [[25, "B"], [50, "C"], [100, "A"]] 
d = c.transpose 
    #=> [[25, 50, 100], ["B", "C", "A"]] 
marks, names = d 
    #=> [[25, 50, 100], ["B", "C", "A"]] 
marks 
    #=> [25, 50, 100] 
names 
    #=> ["B", "C", "A"] 
{ student_name: names, total_marks: marks } 
    #=> {:student_name=>["B", "C", "A"], :total_marks=>[25, 50, 100]} 

Обратите внимание, что два массива транспонированные являются 2xN и NX2, где N есть число студентов. Эти массивы потребуют небольшой памяти, и перенос их будет очень быстрым по сравнению с операцией сортировки, за исключением, возможно, для однокомнатных школ.

Первоначально я использовал sort, а не sort_by(&:first), но изменилось к последнему после прочтения @ комментарий theTinMan на @ ответ Адама, а затем запустить этот простой тест:

require 'fruity' 

n = 100_000 
m = (0..10_000).to_a 
a = Array.new(n) { [m.sample, m.sample] }  

compare do 
    sort { a.sort } 
    sort_by { a.sort_by(&:first) } 
end 

Running each test once. Test will take about 11 seconds. 
sort_by is faster than sort by 5x ± 1.0 (results differ: [[0, 9243], [0, 3145],... 

Как указано, sort и sort_by(&:first) нет те же возвращаемые значения. При сравнении пар кортежей с использованием sort, второй элемент кортежей является тай-брейкером, когда первые элементы равны, тогда как sort_by(&:first) не имеет тай-брейкера.