Массивы и многомерные массивы


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

По ключам идет упорядочивание массива. Отсчет ключа массива начинается с 0. Это важно запомните.
Следовательно в массиве из 10 значений ключи будут (0-9)
Еще стоит заметить, что ключей (индексов) у каждого элемента массива имеется целых два: левосторонний и правосторонний, как и у строк. Примеры массивов:

[] #=> []
array = Array.new #=> []
array = Array[]
array = %w{hello (0..10) 6 7 world} #=> ["hello", "(0..10)", "6", "7", "world"]


Здесь три пустых массива и один содержащий разные типы данных. Как видно из второй строки вышеприведенного примера массив в Ruby является объектом типа Array,то есть экземпляром класса Array. Это четыре основных способа создания массива, сразу скажу, что способ с Array.new применяется редко так как имеет избыточный синтаксис, хотя очень часто его использование имеет явную выгоду, вместо него следует стараться использовать [] — это самый короткий способ создания нового массива. Давайте создадим массив наполненные элементами:

array = ["Welcome", "love", "to", "world"] #=> ["Welcome", "love", "to", "world"]


Только что мы создали массив, который содержит четыре элемента строкового типа. Ruby язык высокого уровня, в котором в отличие от, например, Си не нужно заранее определять размерность массива и тип хранимых в нем данных. Массивы в Ruby могут хранить произвольное количество элементов, которое ограничено лишь оперативной памятью вашего компьютера, при этом данные содержащиеся в массиве могут иметь совершенно разные типы! Как упоминалось ранее. Давайте добавим в наш массив несколько новых значений тех типов данных, с которыми мы уже знакомы:

array[4] = "STRING"
array[5] = (2..5)
array[6] = 12
array[7] = 5.78
array[8] = [1, 2, 3, "hello"]


Теперь проверим, добавились ли элементы массива.

array #=> ["Welcome", "love", "to", "world"]
array[0] #=> "Welcome"
array[5] #=> 2..5
array[8] #=> [1, 2, 3, "hello"]


Вы можете помещать значения в массив не по порядку, пример:

a = []
a[0] = 1
a[5] = 2


При этом будет создан массив не с двумя ключами, а с шестью, при этом промежуточные значения между 0 и 5 будут иметь значение nil, пример:

a #=> [1, nil, nil, nil, nil, 2]


Элементы массивов, сортируются по ключам, то есть чем меньше ключ, тем значение ближе к началу массива, а чем ключ больше, тем ближе к концу.

Прежде, чем мы окунемся в основные методы для работы с массивами, хочу разобрать с вам такой синтаксис объявления массивов, как идентификатор массива %w, который очень похож на то, с чем уже познакомились изучая строки — идентификатором строки %q и другими способами создания массива. Идентификатор массива %w так же позволяет использовать любые пары символов, кроме букв и чисел для помещения в них содержимого массива.

Использование синтаксиса объявления массива с идентификатором %w подходит в тех случаях, когда вы пытаетесь поместить в массив некоторую последовательность элементов разделенных одним лишь пробелом, при этом каждый элемент массива созданного в такой способ будет иметь строковый тип, пример:

a = %w{Welcome love [1,2,3]}
#=> ["Welcome", "love", "[1,2,3]"]




Выше я упоминал о том, что создания массива при помощи Array.new следует избегать, однако есть случаи, когда такой синтаксис может быть очень полезен. Метод .new класса Array способен принимать блок кода, который возвращает элементы помещаемые в массив, таким образом мы можем сэкономить несколько строк кода за счет расчета элементов массива в одной строке с его объявлением, пример:

a = Array.new(10){|elem| elem.odd? ? elem**2 : elem**3}
#=> [0, 1, 8, 9, 64, 25, 216, 49, 512, 81]


В такой простой способ мы создаем массив в десятью элементами, при этом каждый четный элемент содержит значение K в кубе, а каждый нечетный — K в кубе, где K — индекс элемента. Генерация элементов в блоке — это единственный повод использовать синтаксис Array.new, однако многие ссылаются на то, что такой синтаксис более явно указывает на то, что объект, на который ссылается переменная является массивом. .
Синтаксис array = Array[] используется также в целях явного указания типа объекта, что лично я считаю необоснованным.

Самый просто способ создания массива

array = [1,2,3]


Работа с массивами



Обращение к элементам массива:


array = [1,nil,3,nil]
array[0] #=> 1
array[2] #=> 3
array[3] #=> nil

Кроме непосредственно индексов вы можете использовать диапазоны ключей или их перечень, примеры:

array[0,2] #=> [1, 2]
array[0..2] #=> [1, 2, 3]


Ruby очень мощный язык программирования еще и потому, что позволяет вставлять во многие выражения другие выражения, такая вложенность позволяет писать более короткий код, при этом в руках и глазах более-менее опытного программиста код не теряет читабельности, пример:
array[1..array.size] #=> [2, 3]


#bsearch - это новый метод для выполнения бинарного поиска
ary = [0, 4, 7, 10, 12]
ary.bsearch {|x| x >= 6 }


#first, #last — данные методы служат для получения первого и последнего элементов массива и унаследованы от модуля Enumerable, пример:

array.first #=> 1
array.last #=> 3


#pop — данный метод возвращает последний элемент массива (элемент с наибольшим ключом) и удаляет его из массива, пример:


array = [1, 2, 3, "hello!", "...and bye!","opppppps"]
array.pop #=> "opppppps"
array #=> [1, 2, 3, "hello!", "...and bye!"]


#take — метод получает в качестве аргумента число N и возвращает N — первых элементов массива, пример:

[1,5,6,7,10].take(3) #=> [1, 5, 6]


#take_while — данный метод выбирает из массива по одному элементу и передает его в блок кода, пока блок кода не возвращает значение false, метод возвращает элементы массива, пример:

[1,2,3,4,5,4,3,2,1].take_while{|elem| elem [1, 2, 3, 4]


В примере выше мы возвращали все элементы массива до тех пор, пока не наткнулись на первый элемент, который не соответствует условию в блоке кода.

Добавление элементов в массив:

#push — данный метод выполняет абсолютно ту же работу, что и метод <<, то есть добавляет элемент в конец массива, пример:


array = [1, 2, 3, "hello!", "...and bye!"]
array.push("item")
#=> [1, 2, 3, "hello!", "...and bye!", "item"]


#insert - очень полезный метод, который принимает пары аргументов ключ — значение и вставляет значения в массив с указанным ключом, при этом происходит сдвиг всех существующих элементов массива, пример:

array = [1,2,3,4,5]
array.insert(0,100) #=> [100, 1, 2, 3, 4, 5]
array.insert(0, 1, 1, 13) #=> [1, 1, 13, 100, 1, 2, 3, 4, 5]


#unshift — данный метод получает набор значений и помещает их в начало массива, при этом имеющиеся в массиве элементы сдвигаются в право, то есть их левосторонние индексы увеличиваются, пример:

a = [1,2,3,4,5]
a.unshif("a", "b", "c")
a.unshift("a", "b", "c") #=> ["a", "b", "c", 1, 2, 3, 4, 5]
a #=> ["a", "b", "c", 1, 2, 3, 4, 5]


Подсчет элементов массива

#size и #length — данные методы служат для возвращения количества элементов массива:

[1,2,3,4].length #=> 4
[1,2,3,4].size #=> 4
array = []
array [1] = 1
array.size #=> 2
array.length #=> 2
array[1000] = 'one thouthand'
array.size #=> 1001
array.length #=> 1001


#count — данный метод принадлежит модулю Enumerable, о котором мы поговорим позже в этой главе. Метод #count используется также, как #size и #length, однако может выполнять параметризированный подсчет, пример:

array = [1,2,3,4,5,6,7,1,1,2,3]

array.count #=> 11
array.count(1) #=> 3
array.count(2) #=> 2
array.count{|elem| elem.even?} #=> 4
array.count{|elem| elem >= 5 } #=> 3



Сортировка массива

#sort - данный метод служит для сортировки массива, если быть совсем точным, то данный метод возвращает новый массив с отсортированными элементами, а для сортировки самого массива следует использовать соответствующий BANG метод #sort!, примеры:

array = [4, 5, 7, 8, 1, 5, 3]
array.sort #=> [1, 3, 4, 5, 5, 7, 8]
array #=> [4, 5, 7, 8, 1, 5, 3]
array.sort! #=> [1, 3, 4, 5, 5, 7, 8]
array #=> [1, 3, 4, 5, 5, 7, 8]


["a", "b", "e", "d", "c"].sort #=> ["a", "b", "c", "d", "e"]
["a", "b", "e", "d", "c", 1, 4, 2, 3].sort #=> ArgumentError: comparison of String with 3 failed


Мало кто знает, что метод #sort позволяет также сортировать вложенные массивы, давайте рассмотрим пример такого использования:

array = [ [1,5], [1,3,4] , [2,5,6] , [1,2] , [12,6,8] , [1,7] , [1,2,3], [1,1,2] ]
array.sort! #=> [[1, 1, 2], [1, 2], [1, 2, 3], [1, 3, 4], [1, 5], [1, 7], [2, 5, 6], [12, 6, 8]]


Как видно из примера сортировка элементов — массивов происходит сразу по двум критериям: по N — элементу массива и количеству элементов. Массив с наименьшим значением N элемента будет всегда ближе к началу root-массива (родительского массива), чем вложенный массив с большим значением соответствующего по порядку элементов, если во вложенных масcивах порядок следования элементов равен, то первым будет тот, который содержит меньшее число элементов. Такой порядок сортировки свойственен, например, различным словарям.

Метод #sort может выполнять как сортировку от меньшего к большему, так и наоборот, для этого методу #sort необходимо передать блок кода, в которому будет описано правило, например:

array = [1,6,7,9,2,3,7,4]
array.sort{|a,b|ab} #=> [1, 2, 3, 4, 6, 7, 7, 9]
array.sort{|a,b|ba} #=> [9, 7, 7, 6, 4, 3, 2, 1]


Метод #sort получает из массива два рядом идущих элемента a и b и сравнивает их при помощи уже знакомого вам метода , из результатом сравнения становится понятно, следует ли поменять позиции элементов, или необходимо оставить каждый на своем месте.

#sort_by — очень мощный метод, который предоставляет сортировку массива по определенному параметру, который передается в блоке, примеры:

[1,-6,2,-4,3].sort_by{|a| a} #=> [-6, -4, 1, 2, 3]
[1,-6,2,-4,3].sort_by{|a| a**2} #=> [1, 2, 3, -4, -6]


Метод #sort_by получает по одному элементу массива и выполняет и выполняет с каждым из них определенное действие, по результату которого, происходит сортировка, еще пример с сортировкой по количеству символов в строке:

['New York','Atlanta', 'Washington', 'Detroit'].sort_by{|string| string.length}
#=> ["Detroit", "Atlanta", "New York", "Washington"]


#reverse — данный метод возвращает новый массив с элементами отсортированными в противоположном порядке, пример:

a =[1,2,3] #=> [1, 2, 3]
a.reverse #=> [3, 2, 1]
a #=> [1, 2, 3]
a.reverse! #=> [3, 2, 1]
a #=> [3, 2, 1]


#shuffle — выполняет перемешивание элементов массива случайным образом, пример:

a = [1,2,3,4,5]
a.shuffle #=> [3, 4, 1, 2, 5]
a.shuffle #=> [3, 4, 5, 2, 1]
a #=> [1, 2, 3, 4, 5]
a.shuffle! #=> [2, 4, 5, 3, 1]
a #=> [2, 4, 5, 3, 1]


Арифметика множеств

+ — данный метод позволяет произвести сложение двух массивов в один, элементы прибавляемого массива будут добавлены в конец новообразованого пример:

[1,2,4,5, "hello"] + [5,6,[1,2,3]] #=> [1, 2, 4, 5, "hello", 5, 6, [1, 2, 3]]


* — умножение массива, пример:

[1,2,3]*3 #=> [1, 2, 3, 1, 2, 3, 1, 2, 3]


- — разность массивов A — B, в результате этой операции возвращается массив уникальных элементов массива A, пример:

[1,2,3,3,4,4,5] - [1,2,3,5,6] #=> [4, 4]


— сравнение массивов как множеств, сравнивая массивы A и B мы можем получить три результата сравния: 1 — множество A включает множества B, 0 — множество A и B равны, -1 — множество B включает один и более элементов не входящих в множество A. Стоит упомунять, что порядок следования элементов имеет значение, то есть [1,2] и [2,1] не будут считаться одинаковыми, примеры:

[1,2,3,5] [1,2,6] #=> -1
[1,2,3,5] [1,2] #=> 1
[1,2,3,5] [1,2,3] #=> 1
[1,2,3,5] [1,2,3,5] #=> 0
[1,2,3,5] [1,2,5,3] #=> -1


== — строгое сравнение массивов, строгим я его называю потому, что такое сравнение вернет true только тогда, когда массивы имеют одинаковые элементы, одинаковое их количество и одинаковый порядок следования, во всех остальных случаях будет получено значение false, пример:

[1,2,3] == [1,2,3] #=> true
[1,2,3] == [1,2,3,4] #=> false
[1,2,3] == [1,2,4] #=> false
[1,2,3] == [1,2] #=> false


& — пересечение множеств, данный метод возвращает массив совпадающих элементов в нескольких множествах, примеры:

[1,2,6,9,100] & [2,34,100] #=> [2, 100]
[1,2,6,9,100] & [2,34,100] & [1,100] #=> [100]


|Способ соединить два массива, не дублируя повторяющиеся элементы

array1 = [ 1, 2, 3 ]
array2 = [ 2, 4, 5 ]

array3 = array1 | array2 #=> [1, 2, 3, 4, 5]



Удаление и чистка массива

#shift — метод возвращает значение первого элемента массива и удаляет его, пример:

a = [1,2,3,4]
a.shift #=> 1
a #=> [2, 3, 4]

#delete_at - метод принимает индекси элементов, которые следует удалить и удаляет их, пример:

a = [1,2,3,4,5]
a.delete_at(1) => 2
a.delete_at(0) #=> 1
a.delete_at(2) #=> 5
a #=> [3, 4]


#drop — метод используется для возвращения нового массива с удаленным N — числом элементов с начала массива, пример:

a = [1,2,3,4,5,"bingo"]
a.drop(3) #=> [4, 5, "bingo"]
a.drop(a.size-1) #=> ["bingo"]
a #=> [1, 2, 3, 4, 5, "bingo"]


#drop_while — данный метод похож на метод #drop c тем лишь отличием, что вместо удаления N-количества первых элементов, он получает блок кода с условием и удаляет элементы начиная с начала массива до тех пор, пока условие в блоке верно, пример:

a = [1,2,3,4,5]
a.drop_while{|elem| (elem**2) [5]
a.drop_while{|elem| (elem**2) [3, 4, 5]


#delete — данный метод принимает пример объекта для удаления и удаляет все соответствующие примеру элементы массива, пример:

a = ["hello",1,2,3]
a.delete("hello") #=> "hello"
a #=> [1, 2, 3]


#delete_if - удаление элементов массива. Данный метод пробегается по массиву передавая его элементы в блок кода, когда блок кода возвращает true, то элемент удаляется, пример:

cities = ["California", "Washington", "Detroit", "New York", "Philadelphia"]
cities.delete_if{|city| city.length > 8} #=> ["Detroit", "New York"]
[/code]
[b]#uniq[/b] — данный метод производит удаление всех не уникальных элементов массива, таким образом каждое значение в массиве содержится только один единственный раз, пример:

[code]a = [1,2,3,3,2,5]
a.uniq #=> [1, 2, 3, 5]
a #=> [1, 2, 3, 3, 2, 5]
a.uniq! #=> [1, 2, 3, 5]
a #=> [1, 2, 3, 5]


#compact — данный метод позволяет удалить все элементы массив значение которых nil, пример:

a = []
a[0] = 1 #=> 1
a[5] = 2 #=> 2
a #=> [1, nil, nil, nil, nil, 2]
a.compact #=> [1, 2]
a #=> [1, nil, nil, nil, nil, 2]
a.compact! #=> [1, 2]
a #=> [1, 2]


#clear — данный метод произвоодит полную зачистку массива, пример:

a = [1,2,3,4,5]
a.clear
a #=> []


Особое форматирование массивов

#flatten — метод производит «схлопывание» массива. Под «схлопыванием» массива подразумевается приведение многомерного массива в одномерный вид, пример:

a= [1,2,[3,4,[5,6,7],[8,9]],[10,[11,12]]]
a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
a.flatten(1) #=> [1, 2, 3, 4, [5, 6, 7], [8, 9], 10, [11, 12]]
a.flatten(2) #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
a #=> [1, 2, [3, 4, [5, 6, 7], [8, 9]], [10, [11, 12]]]
a.flatten! #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]


В качестве аргумента метод #flatten принимает максимальный уровень вложенности массива, который необходимо «схлопнуть».

#transpose — транспонирование массива. Еси представить двуметный массив в виде таблицы, где вложенные массивы являются столбцами, то метод #transpose предоставляет возможность изменьсть структуру массива, на такую, где столбцы преобразуются в стоки. Словами объяснить это сложновато, вам следует взглянуть на пример, чтобы понять лучше:

a = [[10,11,12],[20,21,22]]
a.transpose #=> [[10, 20], [11, 21], [12, 22]]


Вместо двух столбцов по три строки образовалось три строки по два столбца.


Многомерные массивы:

В ruby нет стандартной поддержки многомерных массивов, но это не проблема, потому что вы всегда можете поставить gem 'narray'
Чтобы установить его себе, выполните команду в консоле
gem install narray no-doc

Где no-doc ключ, чтобы не скачивать документацию, которую можно посмотреть на сайте.

Далее в нашем проекте используем строку
require 'narray'


Использование narray
NArray.new(typecode, size, ...) create new NArray. initialize with 0.
NArray.byte(size,...) 1 byte unsigned integer
NArray.sint(size,...) 2 byte signed integer
NArray.int(size,...) 4 byte signed integer
NArray.sfloat(size,...) single precision float
NArray.float(size,...) double precision float
NArray.scomplex(size,...) single precision complex
NArray.complex(size,...) double precision complex
NArray.object(size,...) Ruby object
all above method initialize with 0 or nil.


Для примера создадим двумерный массив чисел
narray = NArray.int(3,3)
narray.class #=> NArray
narray #=> NArray.int(3,3):
[ [ 0, 0, 0 ],
[ 0, 0, 0 ],
[ 0, 0, 0 ] ]



#to_na преобразование строки или массива в класс NArray
array = %w{hello my world}
narray = NArray.to_na(array)
narray.class #=> NArray