Using linked lists in javascript - javascript

Are there any adventage of using linked lists in javascript? Its main adventage over arrays (for example) is that we can insert element at random index without moving every element and that they are not limited to size as arrays.
However, arrays in JS are dynamically expanded, shrink, and arrays are faster to access data. We can also use Array.prototype.splice() method (indeed linked lists could be still faster than this one) to insert data.
Are there any advantages (speed and so on) of using linked lists over arrays in JavaScript then?
Code of basic linked lists using JS.
function list() {
this.head = null;
this.tail = null;
this.createNode=function(data) {
return {data: data, next: null }
};
this.addNode=function(data) {
if (this.head == null) {
this.tail = this.createNode(data);
this.head = this.tail;
} else {
this.tail.next = this.createNode(data);
this.tail = this.tail.next;
}
};
this.printNode=function() {
var x = this.head;
while (x != null) {
console.log(x.data);
x = x.next;
}
}
}
var list = new list();
list.addNode("one");
list.addNode("two");
list.printNode();

In a linked list if you are prepending or appending elements at the front or at the back then the time complexity is O(1), however it is O(n) for an array. However if you are retrieving an element from an array using the index then the time complexity will be O(1) against the linked list which would be O(n).
So it depends as to what you are trying to do, you need to create benchmarks and then test it as to which operation is taking how much time.
You can check the wiki:

I don't know the performance differences. As you say, linked lists have advantages over arrays in other languages in terms of memory allocation, garbage collection, sparseness, but Javascript arrays handle some of those problems. Nevertheless you still may have reason to use linked lists if your use case calls for that kind of data structure: that is, you only need to reach items starting from the front (or either end with doubly-linked lists) and proceeding from from item to next item, without the need for random access by array index.
Some colorful metaphors about linked lists here: What is a practical, real world example of the Linked List?

Related

Is this O(N) approach the only way of avoiding a while loop when walking this linked list in Javascript?

I have a data structure that is essentially a linked list stored in state. It represents a stream of changes (patches) to a base object. It is linked by key, rather than by object reference, to allow me to trivially serialise and deserialise the state.
It looks like this:
const latest = 'id4' // They're actually UUIDs, so I can't sort on them (text here for clarity)
const changes = {
id4: {patch: {}, previous: 'id3'},
id3: {patch: {}, previous: 'id2'},
id2: {patch: {}, previous: 'id1'},
id1: {patch: {}, previous: undefined},
}
At some times, a user chooses to run an expensive calculation and results get returned into state. We do not have results corresponding to every change but only some. So results might look like:
const results = {
id3: {performance: 83.6},
id1: {performance: 49.6},
}
Given the changes array, I need to get the results closest to the tip of the changes list, in this case results.id3.
I've written a while loop to do this, and it's perfectly robust at present:
let id = latest
let referenceId = undefined
while (!!id) {
if (!!results[id]) {
referenceId = id
id = undefined
} else {
id = changes[id].previous
}
}
The approach is O(N) but that's the pathological case: I expect a long changelist but with fairly frequent results updates, such that you'd only have to walk back a few steps to find a matching result.
While loops can be vulnerable
Following the great work of Gene Krantz (read his book "Failure is not an option" to understand why NASA never use recursion!) I try to avoid using while loops in code bases: They tend to be susceptible to inadvertent mistakes.
For example, all that would be required to make an infinite loop here is to do delete changes.id1.
So, I'd like to avoid that vulnerability and instead fail to retrieve any result, because not returning a performance value can be handled; but the user's app hanging is REALLY bad!
Other approaches I tried
Sorted array O(N)
To avoid the while loop, I thought about sorting the changes object into an array ordered per the linked list, then simply looping through it.
The problem is that I have to traverse the whole changes list first to get the array in a sorted order, because I don't store an ordering key (it would violate the point of a linked list, because you could no longer do O(1) insert).
It's not a heavy operation, to push an id onto an array, but is still O(N).
The question
Is there a way of traversing this linked list without using a while loop, and without an O(N) approach to convert the linked list into a normal array?
Since you only need to append at the end and possibly remove from the end, the required structure is a stack. In JavaScript the best data structure to implement a stack is an array -- using its push and pop features.
So then you could do things like this:
const changes = [];
function addChange(id, patch) {
changes.push({id, patch});
}
function findRecentMatch(changes, constraints) {
for (let i = changes.length - 1; i >= 0; i--) {
const {id} = changes[i];
if (constraints[id]) return id;
}
}
// Demo
addChange("id1", { data: 10 });
addChange("id2", { data: 20 });
addChange("id3", { data: 30 });
addChange("id4", { data: 40 });
const results = {
id3: {performance: 83.6},
id1: {performance: 49.6},
}
const referenceId = findRecentMatch(changes, results);
console.log(referenceId); // id3
Depending on what you want to do with that referenceId you might want findRecentMatch to return the index in changes instead of the change-id itself. This gives you the possibility to still retrieve the id, but also to clip the changes list to end at that "version" (i.e. as if you popped all the entries up to that point, but then in one operation).
While writing out the question, I realised that rather than avoiding a while-loop entirely, I can add an execution count and an escape hatch which should be sufficient for the purpose.
This solution uses Object.keys() which is strictly O(N) so not technically a correct answer to the question - but it is very fast.
If I needed it faster, I could restructure changes as a map instead of a general object and access changes.size as per this answer
let id = latest
let referenceId = undefined
const maxLoops = Object.keys(changes).length
let loop = 0
while (!!id && loop < maxLoops) {
loop++
if (!!results[id]) {
referenceId = id
id = undefined
} else {
id = changes[id].previous
}
}

Slow Binary Search Tree Insertion

I have been creating some data structures to keep my skills sharp. I created a BST and have been stress testing its speed against an array. I noticed that the insertion speed for the BST is far slower than the array.push. I have been using Math.random and adding millions of numbers to both data structures. Oddly the BST is much faster at finding a value then the array.includes/indexOf. Is there a better way to code my insert function or is inserting just a slow part with BST
Here is my code
insert(data) {
if(this.root === null) {
this.root = new Node(data)
return this
}
let current = this.root
while(current) {
if(current.data === data) {
console.log(data + ' Already exists in tree')
return
}
if(data < current.data) {
if(current.left === null) {
current.left = new Node(data)
this.size++
return this
}
current = current.left
}
if(data > current.data) {
if(current.right === null) {
current.right = new Node(data)
this.size++
return this
}
current = current.right
}
}
}
Your code is perfectly fine.
TL;DR: Your comparison/performance benchmark is inaccurate
My answer assumes you are familiar with the Big O notation for time measurement of algorithms, if you aren't do say so in a comment and I will edit my answer.
The thing is, array.push is a simple operation since it always appends the element at the end of the array, which takes O(1) (constant) time, while inserting an element into a BST means you are looking for the right place to insert it, because it has to be in order, you don't just chuck it at the end of the tree like you do with array,push. This operation takes more time (O(logn) where n is the number of nodes in the tree, to be precise), so if you compare these two, of course array.push will be faster.
If you tried inserting an element in order into the array, it would have been much much slower than inserting it into a BST, because you would have to search each and every element in the array until you come to the right spot, then move everything to fit your new element in the array, which takes O(n) time, where n is the number of elements in the array.
So in conclusion, BST excel at finding and inserting elements in order, and when the order doesn't matter and you can just put the element wherever, array will usually do it faster, so that's why searching in your BST is faster than includes or indexOf for an array

Fastest way to iterate through the oldest items in an array? (Fastest implementation of a queue)

See edit too
I'm working in JavaScript but readable psuedocode of any kind may be able to answer my question.
I'm going to struggle describing this so please feel free to comment clarifications - I will answer them and refine my post accordingly.
Essentially, I have an array that acts as a queue, items are added, and when they are processed they need to be removed and more will eventually be added. What is the fastest way to get the first item in an array when I don't care about the index? I just want to iterate on a first-added basis without having to shift all entries in the array down each time. Also worth mentioning I am not looping through the array, I have a master loop for my app that checks if the array has items on each loop, and if it does, it will grab that data then remove it from the array. Currently to do this quickly I use pop, but now recognize I need to get the oldest item in the queue first each time, not the newest.
For more clarification if needed:
In my app I have blocks of raw data that first need to be ran through a function to be ready to be used by other parts of my app. Each block has a unique ID that I pass to the function in order for it to be parsed. For optimization purposes I only parse the blocks of data as it is needed.
In order to do this, my current system is when I realize I need a block to be parsed, I push its unique ID into an array, then a continuous loop in my app checks said array constantly, seeing if it has items in it. If it does, it pops the last item of the array and passes the unique id into the parsing function.
For performance reasons, on each iteration of the loop, only one block of data can be parsed. The issue arises when multiple blocks of data are in queue array already, and I add more items to the array before the loop can finish passing the already existing ID's in the array to the function. Basically, new ID's that are needed to be parsed are added to the end of the array before my loop can clear them out.
Now, this isn't all too bad because new data is needed somewhat sparsely, but when it is, lots of ID's are added at once, and this is an attribute of the app I can't really change.
Since I'm using pop, the most recently added ID is obviously always parsed first, but I chose this method as I believed it to be the fastest way to iterate a queue like this. However, I've come to realize I would rather parse the oldest items in the list first.
In essence, I'm looking for a way to loop through an array oldest to newest without having to re-organize the array each time. The index of the array is not important, I just need first-added, first-parsed behavior.
For example, I know I could always just pass the 0th item in the array to my function then shift the rest of the entries down, however, I believe having to shift down the rest of the items in the array is too costly to performance and not really worth it. If I'm just dumb and that should have no real-world cost please let me know, but still it seems like a band-aid fix. I'm certain there is a better solution out there.
I'm open to other data structures too as the array only holds strings.
Thank you
EDIT: While doing more googling I'm having a face palm moment and realized the problem I'm describing is a stack vs a queue. But now my question moves to what is the fastest implementation of a queue when the index isn't really of value to me?
The following is the FIFO queue implementation using singly linked list in javascript.
For more information of linked list -> https://www.geeksforgeeks.org/linked-list-set-1-introduction/
// queue implementation with linked list
var ListNode = function(val,next = null){
this.val = val
this.next = next
};
var Queue = function(){
let head = null;
let tail = null;
this.show = function(){
let curr = head;
let q = [];
while(curr){
q.push(curr.val);
curr = curr.next;
}
return q.join(' -> ');
}
this.enqueue = function(item){
let node = new ListNode(item);
if(!head) {
head = node;
tail = node;
} else {
tail.next = node;
tail = node;
}
}
this.dequeue = function(){
if(!head) return null;
else {
let first = head;
head = head.next;
first.next = null;
return first;
}
}
}
var myQueue = new Queue();
myQueue.enqueue(1); // head -> 1
console.log(myQueue.show())
myQueue.enqueue(2); // head -> 1 -> 2
console.log(myQueue.show())
myQueue.enqueue(3); // head -> 1 -> 2 -> 3
console.log(myQueue.show())
myQueue.dequeue(); // head -> 2 -> 3
console.log(myQueue.show())

Why do the following two pieces of code run so differently?

Look at these two pieces of code, the second only add the third line. But time is 84 times. Anybody can explain why?
let LIMIT = 9999999;
let arr = new Array(LIMIT);
// arr.push(1);
console.time('Array insertion time');
for (let i = 1; i < LIMIT; i++) {
arr[i] = i;
}
console.timeEnd('Array insertion time');
let LIMIT = 9999999;
let arr = new Array(LIMIT);
arr.push(1);
console.time('Array insertion time');
for (let i = 1; i < LIMIT; i++) {
arr[i] = i;
}
console.timeEnd('Array insertion time');
The arr.push(1) operation creates a "sparse" array: it has a single element present at index 9999999. V8 switches the internal representation of such a sparse array to "dictionary mode", i.e. the array's backing store is an index→element dictionary, because that's significantly more memory efficient than allocating space for 10 million elements when only one of them is used.
The flip side is that accessing (reading or writing) elements of a dictionary-mode array is slower than for arrays in "fast/dense mode": every access has to compute the right dictionary index, and (in the scenario at hand) the dictionary has to be grown several times, which means copying all existing elements to a new backing store.
As the array is filled up, V8 notices that it's getting denser, and at some point transitions it back to "fast/dense mode". By then, most of the slowdown has already been observed. The remainder of the loop has some increased cost as well though, because by this time, the arr[i] = i; store has seen two types of arrays (dictionary mode and dense mode), so on every iteration it must detect which state the array is in now and handle it accordingly, which (unsurprisingly) costs more time than not having to make that decision.
Generalized conclusion: with JavaScript being as dynamic and flexible as it is, engines can behave quite differently for very similar-looking pieces of code; for example because the engine optimizes one case for memory consumption and the other for execution speed, or because one of the cases lets it use some shortcut that's not applicable for the other (for whatever reason). The good news is that in many cases, correct and understandable/intuitive/simple code also tends to run quite well (in this example, the stray arr.push looks a lot like a bug).

Filter/Search JavaScript array of objects based on other array in Node JS

i have one array of ids and one JavaScript objects array. I need to filter/search the JavaScript objects array with the values in the array in Node JS.
For example
var id = [1,2,3];
var fullData = [
{id:1, name: "test1"}
,{id:2, name: "test2"}
,{id:3, name: "test3"}
,{id:4, name: "test4"}
,{id:5, name: "test5"}
];
Using the above data, as a result i need to have :
var result = [
{id:1, name: "test1"}
,{id:2, name: "test2"}
,{id:3, name: "test3"}
];
I know i can loop through both and check for matching ids. But is this the only way to do it or there is more simple and resource friendly solution.
The amount of data which will be compared is about 30-40k rows.
This will do the trick, using Array.prototype.filter:
var result = fullData.filter(function(item){ // Filter fulldata on...
return id.indexOf(item.id) !== -1; // Whether or not the current item's `id`
}); // is found in the `id` array.
Please note that this filter function is not available on IE 8 or lower, but the MDN has a polyfill available.
As long as you're starting with an unsorted Array of all possible Objects, there's no way around iterating through it. #Cerbrus' answer is one good way of doing this, with Array.prototype.filter, but you could also use loops.
But do you really need to start with an unsorted Array of all possible Objects?
For example, is it possible to filter these objects out before they ever get into the Array? Maybe you could apply your test when you're first building the Array, so that objects which fail the test never even become part of it. That would be more resource-friendly, and if it makes sense for your particular app, then it might even be simpler.
function insertItemIfPass(theArray, theItem, theTest) {
if (theTest(theItem)) {
theArray.push(theItem);
}
}
// Insert your items by using insertItemIfPass
var i;
for (i = 0; i < theArray.length; i += 1) {
doSomething(theArray[i]);
}
Alternatively, could you use a data structure that keeps track of whether an object passes the test? The simplest way to do this, if you absolutely must use an Array, would be to also keep an index to it. When you add your objects to the Array, you apply the test: if an object passes, then its position in the Array gets put into the index. Then, when you need to get objects out of the Array, you can consult the index: that way, you don't waste time going through the Array when you don't need to touch most of the objects in the first place. If you have several different tests, then you could keep several different indexes, one for each test. This takes a little more memory, but it can save a lot of time.
function insertItem(theArray, theItem, theTest, theIndex) {
theArray.push(theItem);
if (theTest(theItem)) {
theIndex.push(theArray.length - 1);
}
}
// Insert your items using insertItem, which also builds the index
var i;
for (i = 0; i < theIndex.length; i += 1) {
doSomething(theArray[theIndex[i]]);
}
Could you sort the Array so that the test can short-circuit? Imagine a setup where you've got your array set up so that everything which passes the test comes first. That way, as soon as you hit your first item that fails, you know that all of the remaining items will fail. Then you can stop your loop right away, since you know there aren't any more "good" items.
// Insert your items, keeping items which pass theTest before items which don't
var i = 0;
while (i < theArray.length) {
if (!theTest(theArray[i])) {
break;
}
doSomething(theArray[i]);
i += 1;
}
The bottom line is that this isn't so much a language question as an algorithms question. It doesn't sound like your current data structure -an unsorted Array of all possible items- is well-suited for your particular problem. Depending on what else the application needs to do, it might make more sense to use another data structure entirely, or to augment the existing structure with indexes. Either way, if it's planned carefully, will save you some time.

Categories

Resources