javascript

Call method by String or Reference

Sometimes you may need to call a method, but you don’t know which one in advance i.e. you want to postpone the decision to runtime.
There are two basic ways to that :

  • Store the method name as a String and build the “call” on the fly when you need it
  • Store a reference to a method in a variable and then use language specific syntax to invoke the method

For example let say you have written multiple standalone tests and you want to run different combination of them based on different needs. In other words you want to parameterize method calls.

Something along the lines :

run_tests -suite=3,5,8
run_tests -suite=2..5

If you have a few combinations you can hard code them, but it is more flexible to “pick” the tests/methods on the fly.

This is just one example of the need for selecting the function/method at the last moment. In most cases you prepare or store the name in a String.

What follows are examples of how to do this in several languages.

Python

There are many different ways to call a function or a method indirectly. Below you can see most of them.

#shortcut of : def say(*a): print(*a)
say = print

#using eval()
eval("say")("hello")
say_hi = "say('hi')"
eval(say_hi)

#call defined functon via locals
locals()["say"]('boo hoo')

#via reference stored in a dict
funs = { 'sayit' : say }
funs['sayit']('sayit')

# ----- and now ... -----

#methods in a string
class StringMethod:
  
  def say(self, *args) : print(*args)
  
  
sm = StringMethod()

#"create" a method
method = getattr(sm,"say")
method("howdy")

#direct assignment
sm_say = sm.say
sm_say('whats sup')

#unbound
sayit = StringMethod.say
sayit(sm,'bye')


-----

hello
hi
boo hoo
sayit
howdy
whats sup
bye

Java

In Java you have to use Reflection. You do it in two steps :

  • First you create a Method out of the String and a description of the parameters
  • Then you call the .invoke() method with the real values

import java.util.*;
import java.lang.reflect.*;


public class MethodReflection {

  public static <ARG> void say(ARG arg) { System.out.println(arg); }

  public void say_hi() { say("hi"); }
  private void sayit(String str) { say(str); }
  
  public void test() throws Exception {
    //get the class that hold the methods you want to call
    Class<?> klass = MethodReflection.class;//Class.forName("MethodReflection");
    
    //get a method w/o arguments
    Method m1 = klass.getMethod("say_hi");// or getDeclaredMethod()
    m1.invoke(this);
    
    //private method requires getDeclaredMethod(), also using one String argument
    Method m2 = klass.getDeclaredMethod("sayit", String.class);
    m2.invoke(this, "hello");
    
    //unknown argument
    Method m3 = klass.getMethod("say", Object.class);
    //first argument null for static method
    m3.invoke(null, "howdy");
    
  }

  public static void main(String[] args) throws Exception {
    MethodReflection mc = new MethodReflection(); 
    mc.test();
  }
}

-----

hi
hello
howdy

JavaScript

say = console.log

eval("say('hi')")

sayit = "say"
window[sayit]('sayit')

-----

hi
sayit

JS: range()

JavaScript does not have a range() operator, but can be implemented using generators :

function* range2(start, end, step) {
	step = step || 1
	yield start;
	if (step > 0 && start >= end) return;
	if (step < 0 && start <= end) return;	
	yield* range(start + step, end, step);
}

function* range(start, end, step) {
	step = step || 1
	if (step > 0) { for (let i = start; i < end; i+= step) yield i; }
	else { for (let i = start; i > end; i+= step) yield i;}
}


console.log([...range2(0,6)])
console.log([...range2(0,10,3)])
console.log([...range2(10,0,-3)])
console.log([...range(0,11,2)])
console.log([...range(10,-1,-2)])

-----

[ 0, 1, 2, 3, 4, 5 ]
[ 0, 3, 6, 9 ]
[ 10, 7, 4, 1 ]
[ 0, 2, 4, 6, 8, 10 ]
[ 10, 8, 6, 4, 2, 0 ]

Combinatorics: (n choose r)

Here is a quick function to calculate (n choose r) i.e. in how many ways you can combine “r” items out of total of “n” items.

def nCr(n,r):
	print(f"n,r: {n}, {r}")
	assert n >= r, "nCr : n < r !"
	r = min(r,n-r)
	if r == 0 : return 1
	numerator = reduce(op.mul, range(n,n-r,-1))
	denominator = reduce(op.mul, range(1, r+1) )
	return numerator//denominator

and here is in JavaScript :

N :
R :
(N R) :

This uses several interesting tricks which I should probably explain in a separate post : generators, range, splat operator.

function* range(start, end, step) {
	step = step || 1
	if (step > 0) { for (let i = start; i < end; i+= step) yield i; }
	else { for (let i = start; i > end; i+= step) yield i;}
}

Math.mul = (a,b) => a * b
  
function nCr(n,r) {
        if (r > n) return 'r > n ??'
	r = Math.min(r,n-r)
	if (r == 0) return 1
	numerator = [...range(n,n-r,-1)].reduce(Math.mul)
	denominator = [...range(1, r+1)].reduce(Math.mul)
	return Math.floor(numerator/denominator)
}

JS: Copy array vs Reference

Normally when you pass Arrays around in JavaScript and in every other language they are passed by reference. That is a sane default, but sometimes you want to use or make a copy, so that original is not modified when we do changes.

Here is how by-reference work :

a = [1,2,3,4,5]

b = a
b[2] = 55

console.log(a)

------

[ 1, 2, 55, 4, 5 ]

… changing b changes a too.

But if we copy the array a stays the same.

Below I present two ways to copy Arrays in JavaScript via slicing or using the splat/triple-dot-operator.

a = [1,2,3,4,5,[6,[7],8],9]

b = a.slice()
b[2] = 55

console.log('slice-copy : ')
console.log(a)

a = [1,2,3,4,5,[6,[7],8],9]

b = new Array(...a)
b[2] = 55

console.log('... copy : ')
console.log(a)

-----

slice-copy : 
[ 1, 2, 3, 4, 5, [ 6, [ 7 ], 8 ], 9 ]
... copy : 
[ 1, 2, 3, 4, 5, [ 6, [ 7 ], 8 ], 9 ]

This also works for objects/hashes

a = { 'key1' : 'val1', 'key2' : 'val2' }

b = a //reference

b['key1'] = 'val55'

console.log(a)

a = { 'key1' : 'val1', 'key2' : 'val2' }

b = { ... a } //copy

b['key1'] = 'val55'

console.log(a)

-----

{ key1: 'val55', key2: 'val2' }
{ key1: 'val1', key2: 'val2' }

Random generation

With this post I’m starting a series where I will show you how to implement a functionality on the same topic in multiple languages.

All such posts will be marked with a tag : (multi-language)

We will start with generating random numbers and strings.

JavaScript

function rand_int(min,max=null) {
	if (max == null) { max = min; min = 0 }
	return Math.round(Math.random() * max) + min
}
const az = 'abcdefgijhklmnopqrstuvwxyzABCDEFGIJHKLMNOPQRSTUVWXYZ'
function rand_string(count,chars=az) {
	return Array.from({length:count}, (_,x) => chars[rand_int(52)]).join('')
}
function rand_nums(min,max,count) {
	return Array.from({length:count}, (_,x) => rand_int(min,max))
}
function rand_numstr(min,max,count) {return rand_nums(min,max,count).join('')}

Java

public static int rand(int max) {
	Random r = new Random();
	return r.nextInt(max);
}

public static String rand_numstr(int len) {
	StringBuilder str = new StringBuilder();
	for (int i = 0; i < len; i++) str.append(rand(10));
	return str.toString();
}

public static String rand_string(String characters, int len) {
    Random rng = new Random();
    char[] text = new char[len];
    for (int i = 0; i < len; i++) {
        text[i] = characters.charAt(rng.nextInt(characters.length()));
    }
    return new String(text);
}

public static String rand_string(int len) {
	return rand_string("ABCDEFGHIJKLMNOPQRSTUVWXYZ",len);
}

Python

import numpy as np
import string

def rand_int(min,max,count=1):
   return np.random.randint(min,max,count)
   
def rand_int2(min,max,count=1):#non-repeatable
   return np.random.choice(range(min,max),count,replace=False)
   
def rand_str(count,opt='num'):
  chars = ''
  if opt == 'lower' : chars = string.ascii_lowercase
  elif opt == 'upper' : chars = string.ascii_uppercase
  elif opt == 'str' : chars = string.ascii_letters
  elif opt == 'all' : chars = string.digits + string.ascii_letters
  else : chars = string.digits  
  return ''.join([chars[rand_int(0,len(chars))[0]] for _ in range(count) ])
   
print(rand_int(0,100,10))
print(rand_int2(0,10,10))   
print(rand_str(10))   
print(rand_str(10,'lower'))   
print(rand_str(10,'upper'))   
print(rand_str(10,'str'))
print(rand_str(10,'all'))   

------

[24 50  6 69 69 89 78 24 32  3]
[2 3 0 9 7 6 8 1 5 4]
2123149924
tzuqwomaqv
VWXNTLMUDO
wIbOSUostQ
RDwgVu9QCH

Theme ⇶ Show/Hide the Side Column

A Side Column is very nice and useful thing. I like it a lot ..and … You are expecting a BUT, right ?

But on articles with intricate layout things get squashed and mangled. And after the initial widgets area you waste useful space. The second column is normally top heavy. Adding more data or widgets does not help much, because nobody looks after 1 page-scroll the right side.

So the solution is to keep the Side Column, but allow the user to Fold and Unfold it. And that is the task at hand. I will explain how I did it with my theme Blogstream, you then can abstract the instructions for your case. BTW, very nice and clean theme and once I’m finished with the tweaks and minify the style.css it will become lean and fast 😉

So lets start …

The first thing is the obligatory research with the browser DevTools. The goal is to find the element/s and/or class/es which you can tweak to hide the column. I had partial success , so I reached out to the creator of the theme, who was nice enough to reply back with the additional info.

So this was going to suck … earlier in my tests I saw that html elements DOM manipulation won’t work as I wanted … I have to manipulate the CSS class rules. Those two :

.col-2cl .main-inner { padding-right: 0; }
.col-2cl .s1 { display: none; }

Have not done that before … but I conjectured there have to be something like DOM for CSS … and yes there is.

The sucky part about CSSDOM (that’s what it is called, clever 😉 ) is that there is no XPATH-thingy for search, everything is linear search through LoL of CSS-Stylesheets and rules.

So I had to find those two rules amongst tens of stylesheets and hundreds of rules per sheet. I was about to forget about it and skedaddle …. it will be slooow. But then again where is the fun. So I persisted and decided to go ahead and do it.

I would not only had to write the code, but also had to integrate it in WordPress.

From before I’ve looked at plugin called WP Coder. Again, a lot of coolness, the plugin allows you to write HTML,JS,CSS and files and then embed them with the short-code in only specific places.
I on the other hand needed the code to be on every page … 1,2,3 … and why not put the short code in the footer.

But before that I had to write the code and test it somehow on Word Press. So I wrote the JS code to toggle the Side column and named it “side_bar” in WP Coder. Then created a test page and embed the short code : . Now I could test w/o breaking the whole site.

  • The code is saved @ /wp-content/uploads/wp-coder/script-1.js

The Code

Lets review the code before I explain how I tested it.

The loopy find_css_rule() function goes trough all the style sheets and rules to find the css-selectors. If it finds them the function returns the indexes, so I can refer back directly. And btw it is not that slow.

That out of the way, the rest is easy. One function to check the state of the Side column, two to Fold/Unfold and finally toggle_sidebar() to unite them all.

All the Functions are specific to my case, but once the search problem is solved the rest is easy, so don’t despair.

function find_css_rule(selector) {
	let s = 0
	for (let sheet of document.styleSheets) {
		let r = 0
		for (let rule of sheet.cssRules) {
			if (rule.selectorText == selector) {
				console.log("Sheet:"+s+", rule:"+r)
				return [s,r]
			}	
			r += 1		
		}
		s += 1
	}
}

function is_sidebar_folded(rule) {
	return rule.style.display == 'none' ? true : false
}

function fold_sidebar(ruleP, ruleD) {
	ruleP.style.paddingRight = '10px'
	ruleD.style.display = 'none'
}

function unfold_sidebar(ruleP, ruleD) {
	ruleP.style.paddingRight = '340px'
	ruleD.style.display = ''
}

function toggle_sidebar() {
	let padding = find_css_rule('.col-2cl .main-inner')
	let display = find_css_rule('.col-2cl .s1')
	if (! (padding && display)) {
		console.error('Could not find the side bar classes !')
		return
	}
	//use the idxs for direct access
	let ruleD = document.styleSheets[display[0]].cssRules[display[1]]
	let ruleP = document.styleSheets[padding[0]].cssRules[padding[1]]

	if (is_sidebar_folded(ruleD)) {
		unfold_sidebar(ruleP,ruleD)
	} else {
		fold_sidebar(ruleP,ruleD)
	}
}

Two final things to mention. I on purpose use verbose function names, so I can lower the probability of name conflict. And second I split the task to many but small Functions. The reason ? It easier for testing, but most importantly follows the KISS principle.

The test

So how did I tested it ?

Now the test page has the code, so I went in Preview mode and launched DevTools (Inspect) and in the Console typed :

  • find_css_rule(‘.col-2cl .main-inner’)

worked flawlessly … then tried several times toggle_sidebar() … charming.

That done I added a link to the Test page for the sake of it

  • <a href=”#” onclick=”toggle_sidebar()”>toggle</a>

that worked too … now I was ready to set it up site-wide by setting up the short-code in the footer

Slowly

  • Appearance / Theme Editor / Open footer.php

carefully somewhere at the end I added the following :

<?php
    echo do_shortcode('[WP-Coder title="side_bar"]');
?>

updated the file and was ready for the last step i.e. setting up button or something so I can visually toggle the Side column.

The obvious choice is to put something in the upper right corner of the first column. The problem is that is the article header space which is simple H1 tag, so I quickly created a table to host the header and my triple arrow character in Inspect mode.

Once I made it work I was ready to mess up page.php this time. Again with caution, I just found :

<header class="entry-header group">
	<h1 class="entry-title"><?php the_title(); ?></h1>
</header>

and replaced it with this :

<header class="entry-header group">
	<table width="100%"><tr>
	<td><h1 class="entry-title"><?php the_title(); ?></h1></td>
	<td align=right>
		<a href="#" onclick="toggle_sidebar()" style="font-size: 35px;text-shadow:0 2px 3px #999c9f; color:#0e7fe3;">⇶</a>
	</td>
	</tr></table>
</header>

Updated the file, tested it … and finito.

TODO

This direct change is a bit jumpy … so I have to figure out how to make a smooth transition.

Also it will be nice the triple arrow to be more animated.

But will leave that for a time when I have more time.

BTW to see the result you have to go to a Page on this site, for now I’m keeping post.php unchanged.

Escaping HTML

If you want to escape HTML symbols in JavaScript code you may think you should use escape() function, but not so fast. You can use this escape() for URL’s, but if you want to render HTML use the function below :

function escapeHTML(str){ return new Option(str).innerHTML }

-----

> escape("<div>")
'%3Cdiv%3E'

> escapeHTML("<div>")
'&lt;div&gt;'

Easy way to calculate summaries

If you want to calculate totals or summaries of data stored in Array, you can use JavaScript functional style capabilities, namely .reduce().

Just create your summary function like this :

//lambda syntax
const isum = (acc,val) => acc + val
function izeros(acc,val) { return val == 0 ? acc + 1 : acc}
function inon_zeros(acc,val) { return val == 0 ? acc : acc + 1}

ary = [1,2,3,0,0,0,0]

console.log(ary.reduce(isum,0))
console.log(ary.reduce(izeros,0))
console.log(ary.reduce(inon_zeros,0))


----
Output:
6
4
3

You can try it here : https://onecompiler.com/javascript/3xjg9tyjt

Managing CSS colors with JavaScript

When you work with colors with JavaScript you have to use the rgb-format. But when you extract colors you get back the color as a string.

So I wrote several utility Functions to manage rgb colors.

function getbg(id) {// get background color
  let el = document.getElementById(id)
  let rgb = window.getComputedStyle(el).backgroundColor
  return rgb
}

function setbg(id,rgb) {// set background color
  let el = document.getElementById(id)
  el.style.backgroundColor = rgb
}

function rgb2ary(rgb_str) {
  return rgb_str.substring(4,rgb_str.length - 1).split(/,\s*/).map(a => Number(a))
}

function ary2rgb(ary){ return 'rgb(' + ary.join(',') + ')' }

function rand3() {
  return Array.from({length:3}, (_,x) => Math.floor(Math.random() * 255))
}

function rand_rgb() {return ary2rgb(rand3())}

here are some examples of how to use them :

> rand3()
[251, 1, 161]

> rand_rgb()
'rgb(25,120,161)'

> rgb2ary(rand_rgb())
[240, 137, 194]

> ary2rgb(rgb2ary(rand_rgb()))
'rgb(66,165,150)'

> setbg('abc',rand_rgb())

> getbg('abc')
'rgb(78, 43, 152)'