LinkedList in JavaScript, how to append changes to list?

LinkedList in JavaScript, how to append changes to list?



I'm running into a referential issue with variables in JavaScript, and I've been banging my head against the wall trying to figure this out.



I'm preparing to teach a class on data structures, and I'm reviewing the material after not looking at it for at least 10 years.



I know Linked Lists in theory, but for some reason, I'm struggling to come up with code that actually works in JavaScript (I chose JavaScript because that's what my class knows best)



This is my code:


let LinkedList =
head: ,
tail:
;

let Node =
data: ,
next:


function isObjectEmpty(obj1)
return Object.keys(obj1).length === 0 && obj1.constructor === Object;


function count(node, counter)
if (node.next)
return 1 + count(node.next, counter);

return counter;


/**
* Adds data to LinkedList
* @param LinkedList list
* @param Node data
*/
function add_node(list, data)
let temp = Object.assign(, Node);
temp.data = data;
temp.next = ;

if (Object.keys(list.head).length === 0)
list.head = temp;
list.tail = temp;
else
list.tail.next = temp;
list.tail = temp;

return list;

function insert(l, n, position)
if (position <= 0)
position = 0;
else
position = position - 1;


var list = Object.assign(, l);
var node = Object.assign(, Node);
node.data = n;

// this only counts elements on the list.
var elements = count(list.head, 0);

if (position > elements)
return list;


var currentPosition = list.head;
var counter = 0;

while (!isObjectEmpty(currentPosition))
if (position === counter)
var tmp = currentPosition;
currentPosition = node;
currentPosition.next = tmp.next;
return list;

currentPosition = currentPosition.next;
counter++;


return list;


// how to use the function
let songs = [
id: '1', name: 'Kamikaze', artist: 'Eminem', releaseDate: '2018-08-31',
id: '2', name: 'despacito', artist: 'Luis Fonsi', releaseDate: '2018-08-31',
id: '3', name: 'La tortura', artist: 'Shakira', releaseDate: '2018-08-31',
id: '4', name: 'Roar', artist: 'Roar', releaseDate: '2018-08-31',
];

let list = Object.assign(, LinkedList);
songs.forEach((song) =>
add_node(list, song); // nothing special, just builds the linkedlist
);

list = insert(list, id: '5', name: 'Havana', artist:'who knows', releaseDate:'2018-01-01', 3);
console.log(list); // new object isn't there.



This function is supposed to insert an element in an arbitrary position in the linked list. It kinda works. The problem is that the returned list keeps the reference to the old object before the re-association.



If you put a debugger in this block:


if (position === counter)
var tmp = currentPosition;
currentPosition = node;
currentPosition.next = tmp.next;
return list;



You can see that I'm actually successfully inserting the new node where I want to.



But if you console.log the list structure, you'll see that the newly inserted Node is nowhere to be found.


console.log


list



I'm not sure where I'm failing or why the list keeps the old references and doesn't follow the new "path".



Any pointers in the right direction is greatly appreciated.





Those Object.assign calls are bogus, it doesn't clone the nested objects.
– Bergi
Sep 2 at 20:55


Object.assign





Can you please also add an example of how you would call the function?
– Bergi
Sep 2 at 20:55





What is esObjetoVacio? And shouldn't you have a null check there?
– Bergi
Sep 2 at 20:56


esObjetoVacio


null





@Bergi I just added more code, that should be a full example reproducing the issue.
– ILikeTacos
Sep 2 at 21:03





Can you try this let temp = JSON.parse(JSON.stringify(Node)); instead of let temp = Object.assign(, Node);? for each of Object.assign. May be the object reference is messing things up.
– binariedMe
Sep 2 at 21:06





3 Answers
3



If you put a debugger in this block:


if (position === counter)
var tmp = currentPosition;
currentPosition = node;
currentPosition.next = tmp.next;
return list;



You can see that I'm actually successfully inserting the new node
where I want to



No, you don't. If we drop the tmp and currentPosition assignment confusion, that bit of code is equivalent to


tmp


currentPosition


if (position === counter)
node.next = currentPosition.next;
return list;



All that happens is that you are copying the tail of the list onto the new node, but you never really insert the node as the next of the one currently in the list. It's missing a


next


currentPosition.next = node;



A couple of other points:


isObjectEmpty


null


null


.isNode



Avoid Object.assign. Your usage is really unidiomatic. In


Object.assign


let temp = Object.assign(, Node);
temp.data = data;
temp.next = ;



you are directly overwriting the values that you just copied from Node - better simplify to using an object literal:


Node


let temp = data, next: ;



In var list = Object.assign(, l); you don't really want to create a new object at all. You are going to mutate the passed in list, so you should just keep that. (If you wanted to make pure functions with immutable data structures, you would have to make all the nodes immutable as well, and for inserting clone the entire list until the desired position).


var list = Object.assign(, l);



If your intention for Object.assign was to create new objects that later might involve other properties (or methods) that you weren't going to overwrite, use factory functions instead.


Object.assign



Don't count the list beforehand. Do the insertion in a single pass, and if you reach the end of the list before the position to inert then return.


count


return




function makeLinkedList()
return head: null, tail: null ;


function makeNode(data, next = null)
return data, next ;


function append(list, node)
if (list.head)
list.tail.next = node;
else
list.head = node;

list.tail = node;


function insert(list, data, position)
if (position < 0) throw new Error("position must be nonnegative");

let newNode = makeNode(data);
let prev = null, cur = list.head;
while (cur != null && position > 0)
prev = cur;
cur = cur.next;
position--;

if (cur == null && position > 0) throw new Error("position must be <= list length")
if (cur == null)
list.tail = newNode;
else
newNode.next = cur;

if (prev == null)
list.head = newNode;
else
prev.next = newNode;






Thanks a lot for the code review!! Your points on Object.assign make a lot of sense, I was trying to make the list immutable and write pure functions but I overlooked the other issue. Greatly appreciated!
– ILikeTacos
Sep 3 at 14:22





@ILikeTacos If you make an immutable list, this will greatly simplify the implementation as you don't need the list object any more - you can instead represent the list by its head node. You don't need the tail reference then. I also would recommend to use recursive append and insert functions then instead of iterative ones.
– Bergi
Sep 3 at 15:28


list


tail


append


insert



You could use a version without Object.assign and work with objects as list and nodes who are created by same named function. This function could be replaced later with instanciable functions for more OOP styled approach.


Object.assign



This proposal takes a node and inset it, depending on the function, at the end of the list or at any place in the list. If no node exists in the list, it is inserted at the beginning of the linked list.




function linkedList()
return head: null, tail: null ;


function node(data, next)
return data, next ;


function insertTail(list, node)
if (list.head)
list.tail.next = node;
list.tail = list.tail.next;
else
list.head = node;
list.tail = node;

return list;


function insertPos(list, node, n)
var temp = list.head,
previous;

if (!temp)
return insertTail(list, node);


while (temp.next && n--)
previous = temp;
temp = temp.next;


node.next = temp;
previous.next = node;

return list;


var songs = [ id: '1', name: 'Kamikaze', artist: 'Eminem', releaseDate: '2018-08-31' , id: '2', name: 'despacito', artist: 'Luis Fonsi', releaseDate: '2018-08-31' , id: '3', name: 'La tortura', artist: 'Shakira', releaseDate: '2018-08-31' , id: '4', name: 'Roar', artist: 'Roar', releaseDate: '2018-08-31' ],
list = linkedList();

songs.forEach(song => insertTail(list, node(song)));
console.log(list);

insertPos(list, node( id: '5', name: 'Havana', artist: 'who knows', releaseDate: '2018-01-01' ), 3);
console.log(list);


.as-console-wrapper max-height: 100% !important; top: 0;



Couple of problems. First, you're replacing a node, not strictly inserting. I think you want your .next to be the tmp node, not tmp.next.


.next


tmp


tmp.next


currentPosition.next = tmp;



Second, you need to keep a reference to the node immediately before the insertion point, so that you can set its next value to the newly inserted node:


next


previousNode.next = node //<- node being the one you're inserting



that's why you're not seeing the difference in your linked list.



*Edit (putting it all together):


var prevNode = null;
while (!isObjectEmpty(currentPosition))
if (position === counter)
var tmp = currentPosition;
currentPosition = node;
currentPosition.next = tmp;
if (prevNode === null) list.head = currentPosition;
else prevNode.next = currentPosition;
return list;

prevNode = currentPosition;
currentPosition = currentPosition.next;
counter++;



To make it safe for inserting into the initial position, we have to have the ability to set the list.head. Otherwise we set the previous node's next property.


list.head


next





but where would I put the previousNode.next? I think I follow what you're saying, and yes I agree, currentPosition.next should be set to tmp not to tmp.next I'm just having trouble following the previousNode comment. I also thought of keeping a reference to a previous element, just wasn't sure how to incorporate it into the solution.
– ILikeTacos
Sep 2 at 21:11


previousNode.next


currentPosition.next


tmp


tmp.next


previousNode





I added some code to show how you could have a reference to the previous node, so that you could change its next link to point to the new item that you're inserting.
– David784
Sep 3 at 14:14


next



Thanks for contributing an answer to Stack Overflow!



But avoid



To learn more, see our tips on writing great answers.



Some of your past answers have not been well-received, and you're in danger of being blocked from answering.



Please pay close attention to the following guidance:



But avoid



To learn more, see our tips on writing great answers.



Required, but never shown



Required, but never shown




By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)