У меня есть массив, который должен содержать одно и то же значение, и если есть несоответствия, я хочу получить индекс этих несоответствий.
Пример:
[1,1,1,1,3,1,1,1,2,1,1,1]
Выход:
[4, 8]
Как лучше всего это сделать в Ruby/Rails?
Подозреваю, что #tally
и #group_by
будут полезны.
Нет, не могу этого предположить. Я просто использовал 1 в качестве примера.
А что, если у тебя есть [1,1,1,2,2,2,3,4,5]
? т. е. в данном случае наиболее распространенным значением являются и 1
, и 2
.
Что вы будете делать с индексами «несовпадений»?
что-то вроде:
def find_non_matching_indexes(array, value)
# Use `each_with_index` to iterate through the array with indexes
array.each_with_index.select do |element, index|
# Select elements where the value is not the given value
element != value
end.map(&:last) # Extract only the indexes from the selected elements
end
Проблема в том, что я не знаю значения заранее. Сначала нам нужно определить основное значение, присутствующее в массиве.
def find_mismatch_indices(arr, main_value)
arr.each_index.select { |i| arr[i] != main_value }
end
my_array = [1, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1]
main_value = my_array.tally.max_by { |_, count| count }.first
result = find_mismatch_indices(my_array, main_value)
puts "Indices of mismatches: #{result.inspect}"
Выход
Indices of mismatches: [4, 8]
Вы можете получить наиболее распространенный элемент с помощью tally и max_by:
arr = [1, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1]
most_common = arr.tally.max_by(&:last).first
#=> 1
tally
возвращает хэш, содержащий количество вхождений каждого элемента ({1=>10, 3=>1, 2=>1}
), max_by(&:last)
возвращает пару с наибольшим количеством вхождений, а first
возвращает значение этого элемента. (при условии, что всегда есть один наиболее распространенный элемент)
Чтобы получить индексы, вы можете использовать filter_map:
arr.each_with_index.filter_map { |e, i| i if e != most_common }
#=> [4, 8]
each_with_index возвращает каждому элементу соответствующий индекс. filter_map
выбирает правдивые результаты из блока, который возвращает индекс элемента, если элемент не равен 1
. (и nil
в противном случае, который отбрасывается)
...или arr.each_index.reject { |i| arr[i] == most_common }
.
@CarySwoveland это еще короче, но мне почему-то не нравится запрашивать массив, который я уже перебираю.
У всех нас есть свои слабости. Не могу заставить себя использовать compact
.
Нам нужно преобразовать данные, чтобы определить, где и как часто встречаются элементы.
> a = [1,1,1,1,3,1,1,1,2,1,1,1]
=> [1, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1]
> b = a.each_with_index.sort_by(&:first).group_by(&:first).transform_values { _1.map(&:last) }
=> {1=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 2=>[10], 3=>[11]}
Затем нам просто нужно исключить все элементы, кроме самого распространенного, и извлечь индексы.
> b.sort_by(&:length)[..-2].map { _1[1][0] }.sort
=> [4, 8]
Я получаю разные результаты при запуске этого кода.
Мы могли бы немного очистить первоначальное формирование хеша, используя a.each_with_index.group_by(&:shift).transform_values(&:flatten)
@ Стефан, какие результаты ты получаешь? Я скопировал и вставил прямо из IRB.
@Крис, я получаю {1=>[11, 1, 2, 3, 10, 0, 5, 6, 7, 9], 2=>[8], 3=>[4]}
за первое выражение и [8, 11]
за второе.
Просто еще одна версия:
[1,1,1,1,3,1,1,1,2,1,1,1]
.each_with_object(Hash.new { |hash, key| hash[key] = [] })
.with_index { |(e, hash), i| hash[e] << i } # collect elements with their index
.values # only return the indexes
.sort_by(&:size) # sort indexes arrays by size
.slice(..-2) # return all but the longest
.flatten # flatten
#=> [4, 8]
Альтернативно .values.then {|a| a.delete(a.max_by(&:size)) && a.flatten }
или производные от, например. (a - [a.max_by(&:size)]).flatten
, a.flatten.difference(a.max_by(&:size))
и т. д.
Если вы используете tally
и max_by
, вы учитываете только один наиболее распространенный вариант.
Предположим, у вас есть эти массивы:
arr1=[1,1,1,1,3,1,1,1,2,1,1,1] # should be [4,8]
arr2=[1,1,1,3,3,3,4,4,4,2] # should be [9]?
arr3=[1,1,1,1,1,1] # should be []?
arr4=[1,1,1,2,2,2,3,3,3] # should be []?
Затем рассмотрите этот подход, который позволяет использовать более одного значения или отсутствие значения как «наиболее распространенного»:
def index_of_minority(arr)
tal=arr.tally.sort_by(&:last).reverse
most_common,rest=tal.partition{|e,cnt| cnt==tal[0][1]}.map{|sa| sa.to_h}
arr.each_with_index.select{|e,i| rest.key?(e) }.map(&:last)
end
Затем с этими массивами:
[arr1,arr2,arr3,arr4].each{|arr| p index_of_minority(arr) }
[4, 8]
[9]
[]
[]
Знаете ли вы заранее, что ищете в массиве элементы, отличные от 1? Или вам нужно сначала выяснить, какой элемент является наиболее распространенным, прежде чем искать несоответствия?