Поддержи Openmeetings

пятница, 2 декабря 2011 г.

JavaScript: как случайным образом выбрать элемент из массива и при этом не повториться



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




Ваша буква: ?Вводите буквы слова в это поле



Общая идея решения

Вначале необходимо ответить на следующие вопросы:

  • какие требуются данные;
  • как хранить данные;
  • где хранить данные.

Нам требуется список заданий, содержащий слова и их описания, а так же информация о том, какие задания уже видел пользователь.

Список заданий – это словарь, где ключом является загаданное слово, а значением – текстовая подсказка-описание. Для хранения заданий выбран ассоциативный массив:


wordDictionary = { 
     'слово1': 'Подсказка для слова 1',  
     'слово2': 'Подсказка для слова 2',
     'словоN': 'Подсказка для слова N'
     };

Для того чтобы однозначно определять, какие задания уже видел пользователь, достаточно сохранять один из следующих классов данных:

  1. задания, пройденные пользователем;
  2. задания, которые пользователь еще не видел.

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

Информацию, связанную с действиями пользователя, удобно хранить на клиенте. Для этого будем использовать cookie. Словарь заданий будем хранить в виде ассоциативного массива.

Разбираем функции

Предположим, что у нас уже определены функции для работы с cookie:

  • GetCookie(id) – считывает значение переменной id;
  • SetCookie(id, data) – записывает значение переменной data в переменную id;
  • IsCookieEnabled() – проверяет, разрешены ли cookie на клиенте.

Опишем функцию setWord(), с помощью которой задание выбирается и отображается на странице:

 
function setWord() {
 // подготавливаем словарь к выбору задания
 initDictionary(); 
 // выбираем случайным образом задание
 var word = getRandomWord();
 // обновляем информацию о заданиях на стороне клиента
 updateUserDictionary();
 // отображаем загаданное слово
 document.getElementById('word').innerHTML = word;
 // отображаем подсказку
 document.getElementById('hint').innerHTML = wordDictionary[word];
}
Данная функция состоит из последовательности вызовов вспомогательных функций, а так же действий, связанных с выводом загаданного слова и его описания на страницу. Поскольку для хранения списка заданий используется ассоциативный массив, по загаданному слову легко можно получить подсказку-описание к этому слову (обращение к элементу словаря по ключу).

Логика функции initDictionary() построена следующим образом:

  1. пытаемся считать строку новых заданий из переменной на клиенте;
  2. если попытка не удалась, формируем массив слов из ключей списка заданий и пытаемся сохранить этот массив в виде строки на клиенте, иначе формируем массив слов из строки на клиенте.
Новые задания для пользователя хранятся на клиенте в виде строки слов, разделенных символом «-» (это символ не должен встречаться в загадываемых словах). Из строки такого вида можно легко получить массив, используя метод split, а массив можно легко преобразовать к строке данного вида (метод join). В результате работы функции формируется массив «новых» слов userDictionary.

Ниже представлен код функции:


function initDictionary() {
 var i = 0;
 var wordArrayFromCookies = GetCookie("words");
 if (wordArrayFromCookies == null)
 {
  for (var word in wordDictionary)
  {
   // формируем массив «новых» слов
   userDictionary[i] = word;
   i++;
  }
 SetCookie("words", userDictionary.join("-"));
 } else
 {
  // формируем массив «новых» слов
  userDictionary = wordArrayFromCookies.split("-");
 }
}

Случайный выбор нового задания осуществляется в функции getRandomWord(). Вначале случайным образом выбирается индекс, значение которого не выходит за пределы массива новых заданий (массив userDictionary). Элемент с выбранным индексом удаляется из массива (метод splice). Результатом работы функции является выбранное слово.

Ниже представлен код функции:


function getRandomWord() {
 var word = "";
 var currentWordIndex = Math.floor(Math.random() * userDictionary.length);
 word = userDictionary[currentWordIndex];
 userDictionary.splice(currentWordIndex,1);
 return word;
}

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

Ниже представлен код функции updateUserDictionary(), выполняющей обновление данных на клиенте:


function updateUserDictionary() {
 if (IsCookieEnabled())
 {
  if (userDictionary.length == 0)
  {
   DeleteCookie("words");
  } else
  {
   SetCookie("words", userDictionary.join("-"));
  }
 }
}

Преимущества и недостатки решения

Предлагаемое решение позволило избавиться от лишних действий, нам не требуется организовывать двойной цикл для устранения повторов при выборе заданий. Для данного решения можно достаточно легко изменить способ хранения списка заданий. В данном примере использовался ассоциативный массив. Задания можно так же хранить в виде скрытого элемента объектной модели документа (например, в списке), при этом подсказки-описания могут быть проиндексированы поисковыми системами.

Использование cookie налагает ограничение на количество заданий. Размер переменной cookie не может превышать 4Кбайт. Поскольку все слова из списка заданий в какой-то момент должны быть сохранены в cookie, необходимо учитывать это ограничение.


Комментариев нет :

Отправить комментарий