viall

Simple but powerful Map enhancement.

Usage no npm install needed!

<script type="module">
  import viall from 'https://cdn.skypack.dev/viall';
</script>

README

Viall

Simple but powerful Map enhancement.

GitHub repo size GitHub license GitHub top language


Viall is a new, type of variable that tries to be very close to old Map(). In essence, classic Map allow for an association between unique keys and their values, but lack an iterative interface. For example, how can you transform every value or filter the entries in a Map easily? This is the point of the Viall class! We are combining best functionality of Maps and Arrays in one.


Array-like methods

Many of the methods on Viall are based on their namesake in Array. One of them is find:

// Assume we have an array of cars and a viall of the same cars.
someArray.find(car => car.color === 'black');
someViall.find(car => car.color === 'black');

The interface of the callback function is very similar between the two. For arrays, callbacks are usually passed the parameters (value, index, array), where value is the value it iterated to, index is the current index, and array is the array itself. For vialls, you would have (value, key, viall). Here, value is the same, but key is the key of the value, and viall is the viall itself instead.

Methods that follow this philosophy of staying close to the Array interface are as follows:

  • find
  • filter
  • map
  • isEvery - corresponds to the <Array>.every
  • isAny - corresponds to the <Array>.some
  • concat
  • sort - Learn how Vialls are sorted

filter() & map() returns an Array of values instead of a Viall!

Converting to Array

You can easily convert any Viall to Array if you like to:

import Viall from 'viall';

const myViall: Viall<number> = new Viall();
// <TS> Add value type ☝️
// You can add own interfaces, types, etc.
// Viall's values can store any type of data.

myViall.set('A', 1).set('B', 2).set('C', 3);

// Grab everything
const data = myViall.entries();
// => [['A', 1], ['B', 2], ['C', 3]]

// I want just keys!
const keys = myViall.keys();
// => ['A', 'B', 'C']

// How about values? No problem!
const values = myViall.values();
// => [1, 2, 3]

⏱️ Remember that this convertion can take some time on really large amount of values! (like milions of objects)


Extra Utilities

Some methods are not from Array and are completely new to standard JavaScript.

// Return random value from Viall.
myViall.random();

// Grabs first value from Viall.
myViall.first();

// Grabs first 5 values.
myViall.first(5);

// Similar to first(), but from the end.
myViall.last();
myViall.last(2);

// Removes from the viall anything that meets a criteria.
// Sort of like filter, but in-place.
myViall.sweep(book => book.price > 100);

Viall vs. Map

Vialls are generally over 2x times faster than regular Maps and uses less memory. How this is possible?

⚙️ Vialls operates on special, hidden object instead reusing already existing Map(). Each method like get() or set() is in reality a set of instructions that are not exactly the same like in regular Map, but works in very similar way - final result is the same. Like always - there are pros & cons of this idea:

CONS PROS
Key type is limited to number or string
(Automatically rewrites to string type)
Highly effective (faster)
sort() method works only on
values with string type key
Lightweight (less memory usage)
Can easily convert to Array
Supports a large range of filters
Contains extra methods for easier usage
Works without self replication of own data
Full TypeScript support

Let's make simple capacity test

💽 I want to show you the speed/capacity difference. I made really simple capacity test that just loads 1,000,000 keys & values (ints) into both - Map & Viall

// Clear, before test (pure Node.js)
rss: 18MB
heapTotal: 4MB
heapUsed: 3MB
external: 0MB
arrayBuffers: 0MB

// Using regular Map
rss: 73MB
heapTotal: 59MB
heapUsed: 43MB
external: 0MB
arrayBuffers: 0MB

// Using Viall
rss: 41MB
heapTotal: 31MB
heapUsed: 22MB
external: 0MB
arrayBuffers: 0MB

⏱️ Additionally, I registered times of each loop:

  • Map
    • First try: 421.093ms
    • Second try: 409.127ms
    • Third try: 637.104ms
  • Viall
    • First try: 164.221ms
    • Second try: 114.025ms
    • Third try: 182.431ms

Of course, it strongly depends from type of processor it was run on. I used just MacBook Air with CPU: Intel(R) Core(TM) i3-1000NG4 CPU @ 1.10GHz (4 cores)


❗ Warning ❗

That gonna most likely never happen while normal usage, but you should know that Viall works on original data set instead making self replication over and over like Map does. During my work I never run into any issue with damaging data inside, but for safety, make a copy before you start making brutal operations on larger scale.

Example way how you should use a copy:

interface data {
  health: number,
  mana: number,
  stamina: number
  money: number
  isFighting: boolean
  inventory: Array<...>
}

const characters: Viall<data> = new Viall();

characters.set({...});
characters.set({...});
characters.set({...});

// You can safely read/write, map, filter, etc.
// But you cannot be sure what gonna happen when you push Viall
// To other, unknown 3rd-party property...
dangerousOperation(characters); // ❌ Bad idea

const chars = characters.copy();
dangerousOperation(chars); // 👍 Better
// => Even if you damage data, hey! Thay was just a copy :)