вторник, 22 ноября 2011 г.

JavaScript: Случайные элементы массива

Родилось из C#-овой, но на JavaScript наглядней.

Нужно выбрать из массива N случайных элементов. Как это сделать быстро?

Если длина массива <= N - это очевидно. А если нет? Сначала склонируем массив:
Object.prototype.clone = function() {
var newObj = (this instanceof Array) ? [] : {};
for (i in this) {
  if (i == 'clone') continue;
  newObj[i] = (this[i] && typeof this[i] == "object") ? this[i].clone() : this[i];
  return newObj;
};

Потом создадим новый массив и скопируем в него нужное число элементов, а из оригинала удалим:

function selectRemoveAdd(arr, max)
{
 var arrLength = arr.length;
 if(max >= arrLength)
  return arr.clone();
 var newArray = [];
 var cloneArray = arr.clone();
 var i = 0, newPos = 0;
 while(i++ < max)
 {
  newPos = getRandom(arrLength);
  newArray.push(cloneArray[newPos]);
  cloneArray.splice(newPos, 1);
  arrLength--;
 }
 return newArray;
}
Такой вариант обычно предлагают на форумах. Но зачем создавать ещё одни массив? Можно взять исходный и удалить всё лишнее:
var arrLength = arr.length;
 if(max >= arrLength)
  return arr.clone();
 var newArray = arr.clone();
 while(arrLength-- > max)
  newArray.splice(getRandom(arrLength), 1);
 return newArray;
Какой вариант быстрее? Правильный ответ, что быстрее оба, но по-разному. Если N меньше arr.length / 2, то первый, если больше - то второй. Поэтому самый красивый и правильный способ - это:
function selectRemoveOnly(arr, max)
{
 var arrLength = arr.length;
 var newArray = arr.clone();
 while(arrLength-- > max)
   newArray.splice(getRandom(arrLength), 1);
 return newArray;
}
function selectRemoveAdd(arr, max)
{
 var arrLength = arr.length;
 var newArray = [];
 var cloneArray = arr.clone();
 var i = 0, newPos = 0;
 while(i++ < max)
 {
  newPos = getRandom(arrLength);
  newArray.push(cloneArray[newPos]);
  cloneArray.splice(newPos, 1);
  arrLength--;
 }
 return newArray;
}
function selectOptimised(arr, max)
{
 if(max >= arr.Length)
  return arr.clone();
 if(max <= arr.length / 2)
  return selectRemoveAdd(arr, max);
 else
  return selectRemoveOnly(arr, max);
}
И работать будет просто замечательно:

пятница, 4 ноября 2011 г.

C#: Пишем в Control из Thread

Решений немало.

Простое, как грабли, через делегат:
string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate {
    someLabel.Text = newText; // runs on UI thread
});

То же самое, вынесено в отдельную функцию и выверено по MSDN:

delegate void UpdateLabelDelegate (string message);

void UpdateLabel (string message)
{
    if (InvokeRequired)
    {
         Invoke (new UpdateLabelDelegate (UpdateLabel), new object[] { message });
         return;
    }
    MyLabelControl.Text = message;
}

Там же - замечательные LINQ-оподобные конструкции, которые не так просто вставить в Blogger - там есть Template-овы конструкции вроде < Func <, а Blogger принимает их за теги. Поэтому не забываем ставить пробелы: Очаровательное LINQ-оподобное решение:

private delegate void SetPropertyThreadSafeDelegate(Control @this, Expression< Func > property, TResult value);

public static void SetPropertyThreadSafe(this Control @this, Expression< Func > property, TResult value)
{
var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;

if (propertyInfo == null ||
!@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
@this.GetType().GetProperty(propertyInfo.Name, propertyInfo.PropertyType) == null)
{
throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
}

if (@this.InvokeRequired)
{
@this.Invoke(new SetPropertyThreadSafeDelegate(SetPropertyThreadSafe), new object[] { @this, property, value });
}
else
{
@this.GetType().InvokeMember(propertyInfo.Name, BindingFlags.SetProperty, null, @this, new object[] { value });
}
}

Улучшенные вариант с проверкой на null и более жёсткой типизацией:

public static void SetPropertyInGuiThread< C, V >(this C control, Expression< Func < C, V >> property, V value) where C : Control
{
    var memberExpression = property.Body as MemberExpression;
    if (memberExpression == null)
        throw new ArgumentException("The 'property' expression must specify a property on the control.");

    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (propertyInfo == null)
        throw new ArgumentException("The 'member' expression must specify a property on the control.");

    if (control.InvokeRequired)
        control.Invoke(
            (Action< C, Expression< Func < C, V >>, V>)SetPropertyInGuiThread,
            new object[] { control, property, value }
        );
    else
        propertyInfo.SetValue(control, value, null);
}

среда, 2 ноября 2011 г.

JavaScript: Задачка

Что напришет в лог вот этот код? Ответ обосновать.

a=1/2; if (a = 0,5) console.log(a);

JavaScript: Объекты и необъекты

Говорят, во всём семействе ECMAScript все переменные - псевдообъекты.

Так вот, это неправда.

В JavaScript, например, всего 6 типов объектов:

null, undefined, number, string, boolean и object

а значит, записать в числовую переменную новое свойство - нельзя.

Вызывая оператор "." для number, string, boolean мы просто создаём ещё один object, который получает новое свйоство, а потом записывается в никуда. Правило конвертации простое:
  • если присвоили object - оставляем как есть
  • если присвоили undefined, кидаем exception
  • во всех прочих случаях - new Number(input), new String(input) или new Boolean(input)

вторник, 1 ноября 2011 г.

JavaScript: быстрый floor и приведение объектов

Чудесное от Михаила Барановского:

В JavaScript можно писать через степень:

var a = 120000; // make it shorter?
var a = 12e4;

var a = Math.floor(b);
// если b всегда > 0, можно сократить до:
var a = ~~b;

Легко получать текущую дату:
var date = +new Date;

Это работает, потому что + дёргает valueof.

var a = new Boolean(false); // how to quickly check if (a)?
alert(typeof a); // "object"
alert(!!a); // always true, because a is an object
alert(!!+a);
// right result, because "+" is calling valueOf
// method and convert result to number "0..1"
alert(a>0); // even shorter