Reputation: 39
I am working through the examples in Eloquent Javascript and I cannot seem to generate a working solution to recursively go from an array to a linked list.
The first (iterative) approach seems to work. I am stumped by the recursion. A push in the right direction would be greatly appreciated. Here is the code I have written so far:
function arrayToList(arr) {
var retObj = {};
var current = retObj;
for (var i = 0; i < arr.length; i++) {
current.value = arr[i];
if (arr[i + 1] === undefined) {
current.rest = null;
} else {
current.rest = {};
}
current = current.rest;
}
return retObj;
}
function recArrayToList(arr, obj) {
if (arr.length === 0) {
return 'DONE!';
}
obj.value = arr[0];
obj.rest = {};
return recArrayToList(arr.slice(1), obj.rest);
}
Upvotes: 1
Views: 436
Reputation: 53535
In this example we're building the current item by setting its value to be the first item in the array and the rest we'll set to the result of the recursive call. The stop condition sets the rest
to null
(meaning - we're done:
function recArrayToList(arr) {
if (arr.length === 0) {
return null; // last element
}
var obj = {};
obj.value = arr[0];
obj.rest = recArrayToList(arr.slice(1)); // recursive call
return obj;
}
// example
console.log(recArrayToList([1,2,3]));
The code in the question was close, but there are two mistakes:
obj.rest
the way it should haveUpvotes: 2
Reputation: 350310
The recursive version has some flaws. "Done!" is not really what you want as return value if you expect a linked list.
It is also nicer if you don't have to pass the result object as second argument, as the recursive call should really not have to know the object its value is going to be used in. You can do it without that.
It helps to think that the recursive call returns the rest
value, and that you can inject that immediately in that property as you create the wrapper object as an object literal. And then you find that the code really reduces to just one return statement... which you can combine with the stop condition in a ternary operator:
function recArrayToList(arr) {
return arr.length ? {
value: arr[0],
rest: recArrayToList(arr.slice(1))
} : null;
}
const list = recArrayToList([1, 2, 3]);
console.log(list);
Upvotes: 0
Reputation: 7553
This should work:
function recArrayToList(arr) {
if(arr.length === 0){
return null;
}
return {value: arr[0], rest: recArrayToList(arr.slice(1))};
}
console.log(recArrayToList([1, 2, 3]));
Upvotes: 0
Reputation: 96
Try this:
function recArrayToList(arr, current, retObj) {
var obj = current;
if(retObj){
obj = retObj;
}
if (arr.length === 0) {
return current;
}
obj.value = arr[0];
obj.rest = {};
return recArrayToList(arr.slice(1), current, obj.rest);
}
Upvotes: 0
Reputation: 26557
Since it's a single-linked list (meaning it only goes one way), you'll also need to pass in the root as well. You could have this be a default parameter that you don't need to pass in the first time.
Similarly, you can also make obj
an optional parameter which will self-initialize as well, making your initial call just recArrayToList(someArr)
.
function recArrayToList(arr, obj, root) {
if (arr.length === 0) {
obj.rest = null;
return root;
}
if (!obj) {
obj = {};
}
if (!root) {
root = obj;
}
obj.value = arr[0];
obj.rest = {};
return recArrayToList(arr.slice(1), obj.rest, root);
}
let current = recArrayToList([1,2,3,4,5]);
while(current.rest) {
console.log(current.value);
current = current.rest;
}
You can also crunch it down a bit with some more compact JS (some people don't like some of these conventions):
function recArrayToList(arr, obj, root) {
if (arr.length === 0) {
obj.rest = null;
return root;
}
obj = obj || {};
root = root || obj;
Object.assign(obj, { value: arr.shift(), rest: {} });
return recArrayToList(arr, obj.rest, root);
}
let current = recArrayToList([1,2,3,4,5]);
while(current.rest) {
console.log(current.value);
current = current.rest;
}
Upvotes: 0