JUCE Javascript array has no indexOf()


#1

Is there a reason JUCE Javascript array doesn’t have indexOf? Simple fix:

add under ArrayClass() in file juce_Javascript.cpp

setMethod ("indexOf",  indexOf);

add after var push function:

    static var indexOf(Args a)
    {
        if (Array<var>* array = a.thisObject.getArray())
            return array->indexOf(get(a, 0));

        return var::undefined();
    }

#2

Good request, will add that method, thanks!


#3

array doesn’t have a sort function… I tried adding it, but got build errors complaining about the comparator


#4

You’ll need to define a custom comparator class because the var class has no overloaded < and > operators:

#define RETURN_COMPARE_RESULT(t) {t f = (t)first; t s = (t)second; return (f == s) ? 0 : (f > s ? 1 : -1);}
struct VariantComparator
{
	int compareElements(const var &first, const var &second) const
	{
		if (first.isBool()) RETURN_COMPARE_RESULT(bool)
		if (first.isInt()) RETURN_COMPARE_RESULT(int)
		else if (first.isInt64()) RETURN_COMPARE_RESULT(int64)
		else if (first.isDouble()) RETURN_COMPARE_RESULT(double)
		else if (first.isString()) RETURN_COMPARE_RESULT(String)
		else return 0;
	};
};
#undef RETURN_COMPARE_RESULT

I don’t know if storing the casted primitive values is faster than casting them twice (if I read the var cast overload code correctly casting means a virtual function call), so maybe there are some performance improvements possible.

Add this anywhere you like. Then you can add the sort function to the ArrayClass:

static var sort(Args a)
{
    if (Array<var>* array = a.thisObject.getArray())
    {
        VariantComparator comparator;
        array->sort(comparator);
    }
    
    return var::undefined();
}

And the method registration:

setMethod("sort", sort);

#5

Ouch! Please never use macros in public like that, there may be impressionable beginners reading! A templated function would be more dignified.

But it’s actually not that simple… You need to take into account the types of both the left and right arguments. And the rules for Javascript object comparison are more quirky than what you’ve got there. But there’s no need to re-implement it anyway - the JS parser already handles the < operator, so the code to do this comparison correctly must already exist - if you look inside the parser it’ll be in there somewhere.


#6

And I was so proud for not forgetting to undef …


#7

Alright, next try, no macros and with the more compliant logic from the parser:

struct VariantComparator
{
    int compareElements(const var &a, const var &b) const
    {
        if (isNumericOrUndefined(a) && isNumericOrUndefined(b))
            return (a.isDouble() || b.isDouble()) ? returnCompareResult<double>(a, b) : returnCompareResult<int>(a, b);

        if ((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid()))
            return 0;

        if (a.isArray() || a.isObject())
            throw String("Can't compare arrays or objects");

        return returnCompareResult<String>(a, b);
    };

private:

    template <typename PrimitiveType> int returnCompareResult(const var &first, const var&second) const
    {
        PrimitiveType f = (PrimitiveType)first;
        PrimitiveType s = (PrimitiveType)second;
        return (f == s) ? 0 : (f > s ? 1 : -1);
    }

    bool isNumericOrUndefined(const var &v) const
    {
        return v.isDouble() || v.isInt() || v.isInt64() || v.isUndefined() || v.isBool();
    }
};

If you use it outside the Javascript context, throwing a String() might not be the optimal error handling for you (and in the Javascript context it misses the code location), so you might want to change that line to fit your purpose…


#8

Better! …but that jumps out at me as looking very non-D.R.Y, as it’s close to being a copy-paste of the same logic that’s already used elsewhere.

Also, a quick glance at the docs for the real JS function shows that you’ve overlooked both the return value and the optional argument that it can take, which allows a custom sort function to be provided.

Given that it has a custom sort argument, probably a better approach would be to always use a JS comparison function: either the custom one if present, or a default one. That way the default comparison function would be a simple line of JS compiled using the parser, which would automatically use the existing < operator, rather than needing to re-implement a comparator like you’ve done here.

BTW you should avoid C-style casts in general, but never, never use them with non-primitive types, and particularly in templated code. Always use a static_cast, which will cause errors if you accidentally do something silly.

(Not meaning to sound critical - intending this as friendly coding tuition for you! :slight_smile: )