MYSQL ВЫБОРКА СЛУЧАЙНЫХ ЗАПИСЕЙ (SQL ВЫБРАТЬ ЗАПИСИ РАНДОМОМ
Для того, чтобы выбрать записи из базы данных в случайном порядке достаточно упорядочить их по случайному числу в MySQL.
SELECT * FROM таблица WHERE 'some shit' ORDER BY RAND(NOW()) LIMIT n
где n - количество случайных строк
КАПЧА
Реализация
capcha.rar
СОЗДАЁМ ОДНОМЕРНЫЙ МАССИВ ИЗ МНОГОМЕРНОГО
Всё достаточно просто, если исходный массив у нас двумерный. Возьмём, для примера, такой:
$arrIn = array(
array('a','b','c','d'),
array(1,2,3),
array('e',4,'f',5),
);
В самом обычном цикле, делаем слияние каждого из вложенных массивов с результирующим массивом (изначально пустым), используя функцию array_merge() и записывая результат слияния в этот же конечный массив:
$arrOut = array();
foreach($arrIn as $subArr){
$arrOut = array_merge($arrOut,$subArr);
}
Можно конечно и поизвращаться, но больше для расширения кругозора, чем для реальной практики в данной задаче. Будем использовать две функций: array_map() - применяет callback-функцию к каждому элементу массива и функцию array_merge(), которую рассматривали выше.
$arrOut = array();
array_map(function($a) use(&$arrOut){
$arrOut = array_merge($arrOut,$a);
}, $arrIn);
Немного расшифрую: в качестве callback-функции, мы используем анонимную функцию, которая получает поочерёдно каждый из вложенных массивов в переменную "$a" и сливает с массивом "$arrOut", который в итоге и будет содержать все значения исходного массива, но уже как одномерным. Так как внутри callback-функции массив "$arrOut" будет не виден (если только он не объявлен глобальным), мы используем ключевое слово use, которое позволяет использовать внешние переменные. Но передаём массив не как значение, а как "ссылку", поставив перед переменной символ амперсанда "&". Если бы мы этого не сделали, то каждый раз, мы получали бы пустой массив "$arrOut", который сливали с текущим "$a".
Для новичков всё это пока малопонятно и, пытаясь адаптировать код под свои нужды, могут наделать ошибок. Не пугайтесь - есть решение "в пару строк";) Используем функцию call_user_func_array(), которая, по сути, сделает всё то, что мы делали во втором примере, но, так сказать, одним махом.
$arrOut = call_user_func_array('array_merge', $arrIn);
В результате всех вышеупомянутых вариантов, мы получим массив такого вида:
Array
(
[0] => a
[1] => b
[2] => c
[3] => d
[4] => 1
[5] => 2
[6] => 3
[7] => e
[8] => 4
[9] => f
[10] => 5
)
Хочу отметить, что в случае, если вложенные массивы являются ассоциативными, то при совпадении ключей, последующий элемент будет перезаписывать предыдущий с таким же ключом. Чтобы избежать такой неувязочки и получить все значения, мы немного доработаем наш код функцией array_values():
$arrOut = array();
foreach($arrIn as $subArr){
$arrOut = array_merge($arrOut,array_values($subArr));
}
Остаётся выяснить, какой из трёх вариантов работает быстрее. Я сгенерировал массив, состоящий из сотни вложенных массивов, в каждом из которых по десять элементов и вот средние результаты, которые говорят сами за себя:
Способ |
секунд |
Цикл + array_merge |
0.0109 |
array_map + array_merge |
0.0170 |
call_user_func_array |
0.0009 |
С двумерными массивами немного разобрались, теперь перейдём к более сложной задаче - трехмерные (и более) массивы или многоуровневые массивы с неизвестной вложенностью. И первое решение, которое напрашивается - это использование рекурсии.
$arrIn = array( // исходный массив
'A' => array('first',2,3),
'B' => array(
'b1' => array(4,5,6,7),
'b2' => array('a','b','c')
),
'C' => array(
0 => array(8,9),
1 => array(
'c01' => array(
array(10,11,12),
'last'
)
)
)
);
function makeSingleArray($arr){
if(!is_array($arr)) return false;
$tmp = array();
foreach($arr as $val){
if(is_array($val)){
$tmp = array_merge($tmp, makeSingleArray($val));
} else {
$tmp[] = $val;
}
}
return $tmp;
}
$arrOut = makeSingleArray($arrIn);
Вполне нормальный способ, который, на мой взгляд, не требует каких-то подробных разъяснений и хорошо справляется со своей задачей. Многие, даже не задумываясь, именно его бы и применили. Но я хочу показать, как это же можно сделать буквально парой строк кода, используя "итераторы" из стандартной библиотеки PHP (SPL):
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($arrIn));
$arrOut = iterator_to_array($iterator, false);
Вот и всё! И результат будет, как и в первом случае такой:
Array
(
[0] => first
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
[6] => 7
[7] => a
[8] => b
[9] => c
[10] => 8
[11] => 9
[12] => 10
[13] => 11
[14] => 12
[15] => last
)
Ну, и как в первом случае, показываю среднюю скорость выполнения двух вариантов:
Способ |
секунд |
Рекурсия |
0.2558 |
Классы итераторов |
0.0346 |
Разница в скорости - более, чем в семь раз и кода меньше примерно во столько же! Дальнейшие комментарии излишни... ;)
В общем, вывод можно сделать следующий: создать одномерный массив из многомерного можно достаточно легко, но если еще и хорошо порыться в документации, то "не изобретая свой велосипед", это можно сделать буквально несколькими строками кода и работать будет лучше и быстрее.
НАЙТИ МАКСИМАЛЬНО БЛИЗКОЕ ЗНАЧЕНИЕ
Итак, давайте посмотрим на пару способов того, как можно найти максимально близкое значение в заданном массиве чисел или базе данных. Для начала, разберем вариант на PHP.
function closestNum($arr,$num){
$res = false;
sort($arr);
$less = array_filter($arr, function($a) use ($num){return $a <= $num;});
$greater = array_filter($arr, function($a) use ($num){return $a > $num;});
$min = end($less);
$max = reset($greater);
$avg = (($min + $max) / 2);
$res = 'Число '.$num.' - ';
if($min && $max){
if($num == $min) {
$res .= ' равно числу '.$min;
} else {
$res .= $num == $avg ? 'ровно между '.$min.' и '.$max : 'ближе к '.($num > $avg ? $max : $min);
}
} else {
$res .= ' ближе всего к '.($min ? $min : $max);
}
return $res;
}
// Тестируем
$arr = array(755, 100, 320, 410, 200, 555);
echo closestNum($arr,5); // Число 5 - ближе всего к 100
echo closestNum($arr,320); // Число 320 - равно числу 320
echo closestNum($arr,100.1); // Число 100.1 - ближе к 100
echo closestNum($arr,150); // Число 150 - ровно между 100 и 200
echo closestNum($arr,10000); // Число 10000 - ближе всего к 755
В данном примере, я расписываю ответ функции для наглядности, но а в реальном случае, вам нужно только возвращать само число, которое будет ближе всего к заданному. Теперь давайте пошагово пробежимся по тому, что происходит в этой функции:
Сортируем исходный массив - sort()
Фильтруем массив с помощью функции array_filter(). В первом случае, в переменную "$less" попадут все значения, которые меньше или равны указанному числу "$num", а во втором случае, в переменную "$greater" - только те значения, которые больше числа "$num"
Из первого отфильтрованного массива ("$less"), мы вытаскиваем последний элемент в переменную "$min", а из второго ("$greater") - первый элемент в переменную "$max" с помощью функций end() и reset() соответственно.
Находим среднее арифметическое чисел "$min" и "$max" и присваиваем переменной "$avg", которая нам поможет при сравнении дальше.
Если визуализировать процесс, то получится что-то вроде такого: исходный массив содержит числа "5,2,1,4,3", ищем ближайшее к числу "3", после сортировки массив - "1,2,3,4,5", первый отфильтрованный массив - "1,2,3", второй - "4,5", из первого массива получаем "3", а из второго - "4". Теперь остаётся сравнивать значения, но это, я так думаю, что описывать не нужно ;) Единственное, что нужно отметить - это то, что для версии PHP 5.2 и ниже, функцию array_filter() надо будет записать немного по-другому, т.к. в этих версиях нет поддержки use():
/* ... */
$less = array_filter($arr, create_function('$a, $b='.$num, 'return $a <= $b;'));
$greater = array_filter($arr, create_function('$a, $b='.$num, 'return $a >= $b;'));
/* ... */
Почему так много манипуляций с сравнениями? Причина только в том, что заданное число, может оказаться ровно посередине между двумя значениями в массиве и, если этот момент важен в дальнейших операциях, то приходится использовать и множество сравнений. В противном случае, всю функцию можно записать гораздо короче:
function closestNum($arr,$num){
$tmp = array();
foreach($arr as $val){
$tmp[$val] = abs($val - $num);
}
asort($tmp);
return key($tmp);
}
Мы находим модуль разницы каждого значения массива и заданного числа - abs(), записываем полученное во временный массив "$tmp", где ключами элементов будут сами значения массива, сортируем массив "$tmp" сохраняя ключи - asort() - и возвращаем ключ - key() - первого элемента. В этом случае, если в массиве были значения 100 и 200, а заданное число - 150, то функция нам вернет 100. В остальном, мы получим такой же результат, как и в первом варианте.
А теперь, делая плавный переход, я бы хотел показать интересный способ нахождения ближайшего значения на MySQL, который строится по тому же принципу, как и во втором примере:
SELECT
`num`
FROM
`numbers`
ORDER BY ABS(`num` - 99.99) ASC LIMIT 1
"num" - поле с числовыми значениями, "numbers" - имя таблицы, "99.99" - некое заданное число, к которому и ищем ближайшее значение в базе. Функция ABS() в MySQL - идентична одноименной функции в PHP. Думаю, что комментировать дальше не имеет смысла ;)
Данные способы нахождения ближайшего значения может и не претендуют на какую-то оригинальность, но работают исправно и вполне подходят для задач такого плана. Я буду только рад, если вы предложите альтернативные варианты. Ведь лишних способов не бывает и для каждого можно найти своё применение в той или иной ситуации. Оставляйте свои решения в комментариях и они займут достойное место в коллекции, которая пригодится другим ;)
ПАРСИНГ EXCEL (XML) - ДОКУМЕНТА
Небольшой скрипт для парсинга xml документа - Excel
function parse_excel_xml($xml_file)
{
//Загружаем файл
if($xmlfile = simplexml_load_file($xml_file))
{
//Внешний цикл (строки)
for($y = 0; $y < count($xmlfile->Worksheet->Table->Row); $y++)
{
//Внутренний цикл(столбцы)
for($x = 0; $x < count($xmlfile->Worksheet->Table->Row[$y]->Cell); $x++)
{
//Двухмерный массив
$xml_array[$y][$x] = $xmlfile->Worksheet->Table->Row[$y]->Cell[$x]->Data;
}
}
return $xml_array;
}
else
{
return false;
}
}
Все.
ЗАЧЕМ СИМВОЛ @ (СОБАКА) В PHP?
Когда я знакомился с PHP, то часто встречал значок @ перед переменными. На него я не обращал внимания... Иногда просто убирал его.
Потом узнал что он значит. Вот хочу рассказать теперь вам

@ служит для отключения ошибки, если такая возникнет.
Например, у вас запрос:
$sql='ТУТ ПЕРЕМЕННЫЕДАННЫЕ';
mysql_query($sql,$link);
То вверху страницы выдаст ошибку.
Если будет знак @ перед функциями PHP, то никакой ошибки не будет :)
$sql='ТУТ ПЕРЕМЕННЫЕДАННЫЕ';
@mysql_query($sql,$link);
Так же, "собачку" можно поставить перед
@require_once ENGINE_DIR.'/data/dbconfig.php';
@include_once
и т.д.