Trainto.log()

IT, music, movie, travel, life.. random log of my life

Javascript - Convert String into Number

There are several ways to convert a String into a Number.(or vice versa)

Simple way

// Number to String
let t1 = 5;
t1 += "";

// String to Number
let t2 = '123';
t2 *= 1; // t2 += 0, t2 /= 1, t2 -= 0 all possible

This is the most simplest way.


function Number() and String()

// Number to String
let t1 = 5;
t1 = String(t1);  // '5'

// String to Number
let t2 = '123';
t2 = Number(t2);

let t3 = '10.5';
t3 = Number(t3);  // 10.5


function parseInt() and parseFloat()

let t1 = '5';
t1 = parseInt(t1);

let t2 = '10cm';
t2 = parseInt(t2);  // 10

let t3 = '10.5';
t3 = parseFloat(t3);  // 10.5

API definition of parseInt is like below.

  • parseInt(string, radix)

the radix argument is an integer between 2 and 36 which represents the base in mathematical numeral systems. Have a look at below examples.

parseInt('1101', 2);  // 13
parseInt('11', 10);  // 11
parseInt('11', 16);  // 17

Javascript(ES6) - Map and Set

Before ES6, when we needed Map(keys, values) or Set like any other languages provides, it is a quite pain point. Because javascript does not provide Map or Set, we had to stick to object. However using objects for these purpose has many drawbacks.

ES6 brings us new data structures, Map and Set. So we don’t have be struggling to implement Map and Set using objects.

Map

// Creating a map
const age = new Map();

// Assgin entries by using set() method
age.set('Tom', 30);
age.set('Jessica', 26);
age.set('Jone', 18);

// set() method can be chainable
age
  .set('Abby', 15)
  .set('Ziva', 29);

// This is Javascript, keys and values don't have to be the same types.
age.set(0, 'what?');


Array of arryas can be passed to the constructor to initialize.

const price = new Map([
  ['HHKB', 300],
  ['RealForce', 280]
]);


To search keys and get values:

const age = new Map();
age
  .set('Tom', 30)
  .set('Jessica', 26)
  .set('Jone', 18);

age.has('Tom');  // True
age.get('Tom');  // 30

age.get('Joon');  // undefined

// set() on a key already in the map, the value will be replaced
age.set('Tom', 50);
age.get('Tom')  // 50


Map can be iterable by for…of loop

// keys can be iterable by calling keys() method
for (let k of age.keys()) {
  console.log(k);
}

// values can be iterable by calling values() method
for (let v of age.keys()) {
  console.log(v);
}

// key and value pair can be iterable by calling entries() method
for (let pair of age.entries()) {
  console.log(`${pair[0]} : ${pair[1]}`);
}

The entries() method is the default iterable for a Map, so we can omit it.

For the last, deleting and clearing like below:

const age = new Map();
age
  .set('Tom', 30)
  .set('Jessica', 26)
  .set('Jone', 18);

age.size;  // 3
age.delete('Tom');
age.size;  // 2

age.clear();
age.size;  // 0


WeakMap

A WeakMap has a few different characteristics to Map.

  • Keys have to be objects.
  • Cannot be cleared or iterated.
  • Keys can be garbege-collected.

When the object in a WeakMap does not have any other references to it, it can be garbege-collected. A class in Javascript does not provide private keyword like any other languages(Java, C++, etc.), but using the WeakMap, we can make member invisible.

const Engineer = (function () {
  const skills = new WeakMap();
  
  return class {
    setSkill(skill) {
      skills.set(this, skill);
    }

    getSkill() {
      return skills.get(this);
    }
  }
})();

const e1 = new Engineer();
const e2 = new Engineer();

e1.setSkill('java');
e2.setSkill('kotlin');

e2.getSkill();  // 'kotlin'


Set

A set is a collection of data, does not allow duplication of data.

const numbers = new Set();
numbers.add(1);
numbers.add(2);
numbers.add(3);

numbers.size; // 3

// 1 is already in the set, nothing happens
numbers.add(1);

numbers.size; // 3

// delete() for removing data
numbers.delete(3);

numbers.size;  // 2


There is another data structure introduced by ES6, which is called WeakSet. It is pretty much same with WeakMap except it is not Map, it is Set.

We have Map and Set on our hands with ES6. Do not create objects anymore to pair keys and values, just consider using Map. And sometimes Set is also very useful data structure.

Android - Implementing Zip function using LiveData

The last post, Replace EventBus with LiveData was introduced.

This time, Implementing Zip function(Like Rx) using LiveData will be introduced. To acheive this, MediatorLiveData, which extends LiveData, will be applied.

MediatorLiveData is a LiveData subclass, can observe other LiveData objects and react on LiveData objects’ changes. Basic usage can be found here.

If you’re reading this post, I’m sure that you already know well the concept of Zip function of Rx. Just in case, refer this page for basic concept of Zip function.

As always, start with dependencies configuration.

// This includes LiveDdata and ViewModel
// If you want to add just LiveData, then use 
// "android.arch.lifecycle:livedata:x.x.x"
implementation "android.arch.lifecycle:extensions:1.1.1"


Let’s assume that there is MainViewModel which calls two async retrofit calls.

class MainViewModel : ViewModel() {
    private val name: MutableLiveData<String> = MutableLiveData()
    private val age: MutableLiveData<Int> = MutableLiveData()
  
    fun getZippedLiveData(): LiveData {
        return zip2(name, age) { name: String, age: Int -> "name: $name, age: $age" }
    }

    fun start() {
        // Async calls
        // Asume there is RemoteApi class which implement Retrofit async call
        RemoteApi.getName() {
            it?.let { this.name.value = it }
        }

        RemoteApi.getAge() {
            it?.let { this.age.value = it }
        }
    }
}

This ViewModel calls 2 async remote calls from start(), and result(name, age) will be saved to each MutableLiveData. And View(activity) will observe zipped LiveData, and zipped LiveData will be passed to the View by calling getZippedLiveData().

The View(activity) code will look like below.

class MainActivity : AppCompatActivity() {
    private val vm = ViewModelProviders.of(this).get(MainViewModel::class.java)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // If both name and age are received from the remote calls,
        // then show them in the TextView
        vm.getZippedLiveData().observe(this, Observer {
            it?.let {
                textView.text = it
            }
        })

        vm.start()
    }
}

So far this is typical android MVVM architecture using LiveData and ViewModel. The main point is how the zipped LiveData object can be created at getZippedLiveData() function of MainViewModel.

Now let’s figure out how zipped LiveData can be created.

fun <T1, T2, R> zip2(src1: LiveData<T1>, src2: LiveData<T2>,
                     zipper: (T1, T2) -> R): LiveData<R> {

    return MediatorLiveData<R>().apply {
        var src1Version = 0
        var src2Version = 0

        var lastSrc1: T1? = null
        var lastSrc2: T2? = null

        fun updateValueIfNeeded() {
            if (src1Version > 0 && src2Version > 0 &&
                lastSrc1 != null && lastSrc2 != null) {
                value = zipper(lastSrc1!!, lastSrc2!!)
                src1Version = 0
                src2Version = 0
            }
        }

        addSource(src1) {
            lastSrc1 = it
            src1Version++
            updateValueIfNeeded()
        }

        addSource(src2) {
            lastSrc2 = it
            src2Version++
            updateValueIfNeeded()
        }
    }
}

Above zip2 function takes two LiveData, src1 and src2. These are used as input source for MediatorLiveData to create. And also it takes zipper function(Imagine if this was Java… would be much more complicated!) as its last parameter, and it should describe how the data would be zipped into one object R(See getZippedLiveData() function of MainViewModel). In the updateValueIfNeeded() function, it checks version and null, and then set MediatorLiveData’s value.

Simply The MediatorLiveData created issues(changing value) events only if all input source(src1, src2) are ready.

This example is very simple case, but if you edit zip2 function you like, it can be applied various case. For example src1, src2 parameters can be replaced with vararg.

Maybe you noticed this implementation does not behave exactly same with Rx’s zip. In this implementation, MediatorLiveData only takes the latest pair of input source. To make exactly same with Rx’s zip, input source’s history should be managed in the MediatorLiveData.