Javascript recursively count - javascript

I applied for a job where I was given this task. I had to write a function reversePrint() which returns a reversed array with values from a passed object.
Here is the object
var someList = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
My attempt
function reversePrint(linkedList) {
// empty array with the result
var b = [];
// while loop to check if current next is not null
while ( linkedList.next !== null ) {
// push current value into the array
b.push(linkedList.value);
// go one level deeper
linkedList = linkedList.next;
}
// push the last value into the array
b.push(linkedList.value);
// return reversed array
return b.reverse();
}
The function works, but I have a feeling that there is a better way to do this. I have already searched stackoverflow for javascript recursive operations, but could not find anything that would be considered a duplicate. Is there a more efficient way to do this?

There's nothing fundamentally wrong with your code, but your instinct is right: a recursive solution would seem to match the recursive nature of the data structure better, would be more concise, and also can be written to avoid the reverse.
var someList = {value: 1, next: {
value: 2, next: {
value: 3, next: {
value: 4, next: null}}}};
function reversePrint(input) {
return !input ? [] : reversePrint(input.next).concat(input.value);
}
console.log(reversePrint(someList));
Note that this solution is not tail-optimizable and might be best avoided if the input could be very deep. A tail-optimizable solution would be:
function reversePrint(input) {
return function inner(input) {
return !input ? [] : [input.value].concat(inner(input.next));
}(input).reverse();
}
Avoiding reverse with the iterative solution is possible, of course, but it requires an expensive unshift onto the front of the array at each step. On the other hand, the recursive solutions create multiple arrays of gradually increasing length, which is not exactly cheap either.

Using recursion you can also do it like this.
var someList = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
function reversePrint(linkedList) {
var r = []
if(linkedList.next) r = r.concat(reversePrint(linkedList.next))
r.push(linkedList.value)
return r
}
console.log(reversePrint(someList))

https://jsfiddle.net/zwzecvqf/2/
Here you have a recursive function.
function reversePrint (item) {
var nextItem = item.next
var items
if (nextItem) {
items = reversePrint(nextItem)
} else {
items = [];
}
items.push(item.value)
return items
}

Yet another recursive solution, but avoiding creating and throwing away multiple intermediary arrays:
function reversePrint(node, array) {
// Fill in the array if not given
array = array || [];
// Recurse if appropriate
if (node.next) {
reversePrint(node.next, array);
}
// Add the value on the way out
array.push(node.value);
return array;
}
function reversePrint(node, array) {
array = array || [];
if (node.next) {
reversePrint(node.next, array);
}
array.push(node.value);
return array;
}
var someList = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
console.log(reversePrint(someList));

What about not using recursion? I didn't find information this is not banned.
var someList = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
function reversePrint()
{
var currentNode = someList;
var cacheList = [];
do
{
cacheList.push(currentNode.value);
currentNode = currentNode.next;
}
while(currentNode != null);
cacheList.reverse();
return cacheList;
}
var reverseList = reversePrint(); // [4,3,2,1]

Here using a recursive function to sub objects values
function reversePrint(linkedList) {
// empty array with the result
var b = [];
function addValues(subList){
if(linkedList.next)
addValues(linkedList.next);
b.push(item.value);
}
addValues(linkedList);
return b.revers();
}

Related

Implementing a set that treats object equality on value not reference

I want to be able to implement a Set in javascript that allows me to do something like this:
const s = Set([[1,2,3], [1,2,3], 1, 2, 1]);
s.add([1,2,3]);
console.log(s);
// {[1,2,3], 1, 2}
Of course, since the === operator is used on the set, any object will not equal itself unless a reference to the same object is passed, and so instead of the above we would currently get:
Set(5) { [ 1, 2, 3 ], [ 1, 2, 3 ], 1, 2, [ 1, 2, 3 ] }
Does the following seem like a good way to implement this? What might I be missing or can improve on?
class MySet extends Set {
constructor(...args) {
super();
for (const elem of args) {
if (!this.has(elem)) super.add(elem);
}
}
has(elem) {
if (typeof elem !== 'object') return super.has(elem);
for (const member of this) {
if (typeof member !== 'object') continue;
if (JSON.stringify(member) === JSON.stringify(elem))
return true;
}
return false;
}
add(elem) {
return (this.has(elem)) ? this : super.add(elem);
}
delete(elem) {
if (typeof elem !== 'object') return super.delete(elem);
for (const member of this) {
if (typeof member !== 'object') continue;
if (JSON.stringify(member) === JSON.stringify(elem))
return super.delete(member);
}
return false;
}
}
Assuming the provided objects don't contain values that cannot be stringified to JSON (function, undefined, symbol, etc.) You can use JSON.stringify().
One problem you might encounter is that stringifying { a: 1, b: 2 } doesn't produce the same result as { b: 2, a: 1 }. A fairly easy way to solve this would be to stringify the object and make sure the resulting JSON has properties placed in alphabetical order.
For this we can look to the answer provided in sort object properties and JSON.stringify.
I also think you are over complicating things by only stringifying values if they are an object. Instead you could just stringfy everything, null would result in "null", "string" would result in '"string"', etc. This simplifies the code by a lot. The only restriction then becomes that all values must be a valid JSON value.
// see linked answer
function JSONstringifyOrder(obj, space)
{
const allKeys = new Set();
JSON.stringify(obj, (key, value) => (allKeys.add(key), value));
return JSON.stringify(obj, Array.from(allKeys).sort(), space);
}
class MySet extends Set {
// The constructor makes uses of add(), so we don't need
// to override the constructor.
has(item) {
return super.has(JSONstringifyOrder(item));
}
add(item) {
return super.add(JSONstringifyOrder(item));
}
delete(item) {
return super.delete(JSONstringifyOrder(item));
}
}
const set = new MySet([[1,2,3], [1,2,3], 1, 2, 1]);
set.add([1,2,3]);
set.add({ a: { s: 1, d: 2 }, f: 3 });
set.add({ f: 3, a: { d: 2, s: 1 } });
// Stack Overflow snippets cannot print Set instances to the console
console.log(Array.from(set));
// or unserialized
Array.from(set, json => JSON.parse(json)).forEach(item => console.log(item));

Return no element from `Array.flatMap()` by if condition

I have this code:
const myFunc = function (t) {
return myArray.flatMap(clip =>
(t < clip.start || t < clip.end) ? // Valid objects are returned in this *if* condition
[
{ time: clip.start },
{ time: clip.end }
] : // how to return nothing in this *else* condition. Absolutely nothing?
[
{ },
{ }
]
)
}
The above code used a ternary operator of condition ? exprIfTrue : exprIfFalse.
Currently I'm returning empty objects of { } in the case of exprIfFalse.
How can I return nothing in the case of exprIfFalse? I mean, I want absolutely nothing. I mean no array element.
Why cant you just return an empty array, any how Array.flat will remove those empty array from final code. In your case the array is not empty as [], its an array with two empyty objects as [{}, {}] that will produce two empty objects {}, {} in the final output after Array.flat
You have to return something from flatMap. If you return nothing, the corresponding nodes will be added as undefined. That wont be removed with Array.flat. Best option is to return an empty array as below.
Pseudo Code
const myArray = [1, 2, 3, 4, 5];
const myFunc = function (t) {
return myArray.flatMap(clip =>
(clip % 2 === 0) ? // Valid objects are returned in this *if* condition
[
{ value: clip },
{ value: clip }
] : // how to return nothing in this *else* condition. Absolutely nothing?
[]
)
}
console.log(myFunc());

i wanna return correctly children's object. how can i?

function Ha8(arr, id) {
let result = [];
for(let i = 0; i < arr.length; i++) {
if(Array.isArray(arr[i].children)) {
// if it is a array, it going to be run recursive
result.push(arr[i].children)
const col = Ha8(result[i], id);
if(col === id) {
// find it in array in array
return result
// then return the id object,
} else {
continue; // still can't find.. go ahead!
}
} else if (arr[i]['id']===id) {
return arr[i] // will return valid id object
}
return null // if its none , return null, or parameter id is undefined.
}
}
I m write Intended direction. but its not work..
how can i fix ? give me some tip please.
let input = [
{
id: 1,
name: 'johnny',
},
{
id: 2,
name: 'ingi',
children: [
{
id: 3,
name: 'johnson',
},
{
id: 5,
name: 'steve',
children: [
{
id: 6,
name: 'lisa',
},
],
},
{
id: 11,
},
],
},
{
id: '13',
},
];
output = Ha8(input, 5);
console.log(output); // --> { id: 5, name: 'steve', children: [{ id: 6, name: 'lisa' }] }
output = Ha8(input, 99);
console.log(output); // --> null
I wanna return like that, but only return 'null' ..
need to check children's id and return children's object by using recursive.
so i write like that. but i have no idea..
how to return correctly children id's element?
I will give you an answer using a totally different approach, and using the magic of the JSON.stringify() method, more specifically the replacer optional parameter, which allows the use of a callback function that can be used as a filter.
As you can see, it simplifies a lot the final code. It could also be modified to introduce not only an id, but also any key or value, as I did in my final approach.
EDIT: Following your suggestion, as you prefer your function to be recursive, I recommend you to use the Array.reduce() method. It allows an elegant iteration through all the properties until the needs are met.
Using null as initial value, which is the last argument of the reduce method, it allows to iterate through all fields in the array in the following way:
The first if will always be skipped on the first iteration, as the initial value is null.
The second if will set the currentValue to the accumulator if the property id exists and is equal to the value you are trying to find
The third if, which you could add an Array.isArray() to add a type validation, will check if the property children exists. As it is the last one, it will only work if all the other conditions aren't met. If this property exists, it will call again Ha8Recursive in order to start again the process.
Finally, if neither of this works, it should return null. The absence of this last condition would return undefined if the input id doesn't exist
const Ha8 = (array, inputKey, inputValue) => {
let children = null;
JSON.stringify(array, (key, value) => {
if (value[inputKey] && value[inputKey] === inputValue) {
children = value;
}
return value;
});
return children;
};
const Ha8Recursive = (array, inputKey, inputValue) => {
return array.reduce((accumulator, currentValue) => {
if (accumulator) {
return accumulator;
} else if (currentValue[inputKey] && currentValue[inputKey] === inputValue) {
return currentValue;
} else if (currentValue.children) {
return Ha8Recursive(currentValue.children, inputKey, inputValue);
} else {
return null;
}
}, null)
}
const input = [{"id":1,"name":"johnny"},{"id":2,"name":"ingi","children":[{"id":3,"name":"johnson"},{"id":5,"name":"steve","children":[{"id":6,"name":"lisa"}]},{"id":11}]},{"id":"13"}];
console.log('JSON stringify function');
console.log(Ha8(input, 'id', 5));
console.log('Recursive function')
console.log(Ha8Recursive(input, 'id', 5));

How to deeply remove keys in object?

I have this json object returned from an API that has a few quirks, and I'd like to normalize it so I can process the input the same for every response. These means getting rid of superfluous keys:
Response:
{
_links: {...},
_embedded: {
foo: [
{
id: 2,
_embedded: {
bar: []
}
}
]
}
}
So I'd like to remove all the _embedded keys and flatten it, like so:
{
_links: {...},
foo: [
{
id: 2,
bar: []
}
]
}
This is what I have at the moment, but it only works for the top level and I don't think it'll play well with arrays.
_.reduce(temp1, function(accumulator, value, key) {
if (key === '_embedded') {
return _.merge(accumulator, value);
}
return accumulator[key] = value;
}, {})
Loop in recursion on all of your keys, once you see a key which start with _
simply remove it.
Code:
var
// The keys we want to remove from the Object
KEYS_TO_REMOVE = ['_embedded'],
// The data which we will use
data = {
_links: {'a': 1},
_embedded: {
foo: [
{
id: 2,
_embedded: {
bar: []
}
},
{
id: 3,
_embedded: {
bar: [
{
id: 4,
_embedded: {
bar: []
}
}
]
}
}
]
}
};
/**
* Flatten the given object and remove the desired keys if needed
* #param obj
*/
function flattenObject(obj, flattenObj) {
var key;
// Check to see if we have flatten obj or not
flattenObj = flattenObj || {};
// Loop over all the object keys and process them
for (key in obj) {
// Check that we are running on the object key
if (obj.hasOwnProperty(key)) {
// Check to see if the current key is in the "black" list or not
if (KEYS_TO_REMOVE.indexOf(key) === -1) {
// Process the inner object without this key
flattenObj[key] = flattenObject(obj[key], flattenObj[key]);
} else {
flattenObject(obj[key], flattenObj);
}
}
}
return flattenObj;
}
console.log(flattenObject(data));
So, basically you already have almost all of the code you need. All we have to do is wrap it in a function so we can use recursion. You'll see we only add a check to see if it is an object, if it is, we already have a function that knows how to flatten that object, so we'll just call it again with the key that we need to flatten.
function flatten(temp1) { // Wrap in a function so we can use recursion
return _.reduce(temp1, function(accumulator, value, key) {
if (key === '_embedded') {
return _.merge(accumulator, value);
} else if (value !== null && typeof value === 'object') // Check if it's another object
return _.merge(accumulator, flatten(value)) // Call our function again
return accumulator[key] = value;
}, {})
}
I'll be able to test it in a bit, but this should be what you need.
Got it!
function unEmbed(data) {
return _.reduce(data, function(accumulator, value, key) {
const returnableValue = _.isObject(value) ? unEmbed(value) : value;
if (key === 'embedded') {
return _.merge(accumulator, returnableValue);
}
accumulator[key] = returnableValue;
return accumulator;
}, {});
}
Problem before I was returning return accumulator[key] = returnableValue, which worked out to be return returnableValue.

Build a JSON object from absolute filepaths

I receive (in my angularjs application) from a server a list of directories like this:
['.trash-user',
'cats',
'cats/css',
'cats/images/blog',
'cats/images/gallery']
And I would like to build a javascript variable which looks like this:
[{
label: '.trash-user'},
{label: 'cats',
children: [{
label: 'css'},
{label: 'images',
children: [{
label: 'blog'},
{label: 'gallery'}
]}
]}
}]
The paths are in random order.
Hope somebody has some really elegant solution, but any solution is appreciated!
Edit:
Here is my naive approach, I have real trouble with recursion.
I could only make level 0 to work:
var generateTree = function(filetree){
console.log('--------- filetree -------');
var model = [];
var paths = [];
for(var i=0;i<filetree.length;i++) {
paths = filetree[i].split('/');
for(var j=0;j<paths.length;++j) {
var property = false;
for(var k=0;k<model.length;++k) {
if (model[k].hasOwnProperty('label') &&
model[k].label === paths[0]) {
property = true;
}
}
if (!property) {
model.push({label: paths[0]});
}
}
}
console.log(model);
};
If you want an elegant solution, lets start with a more elegant output:
{
'.trash-user': {},
'cats': {
'css': {},
'images': {
'blog': {},
'gallery': {},
},
},
}
Objects are much better than arrays for storing unique keys and much faster too (order 1 instead of order n). To get the above output, do:
var obj = {};
src.forEach(p => p.split('/').reduce((o,name) => o[name] = o[name] || {}, obj));
or in pre-ES6 JavaScript:
var obj = {};
src.forEach(function(p) {
return p.split('/').reduce(function(o,name) {
return o[name] = o[name] || {};
}, obj);
});
Now you have a natural object tree which can easily be mapped to anything you want. For your desired output, do:
var convert = obj => Object.keys(obj).map(key => Object.keys(obj[key]).length?
{ label: key, children: convert(obj[key]) } : { label: key });
var arr = convert(obj);
or in pre-ES6 JavaScript:
function convert(obj) {
return Object.keys(obj).map(function(key) {
return Object.keys(obj[key]).length?
{ label: key, children: convert(obj[key])} : { label: key };
});
}
var arr = convert(obj);
I'll venture that generating the natural tree first and then converting to the array will scale better than any algorithm working on arrays directly, because of the faster look-up and the natural impedance match between objects and file trees.
JSFiddles: ES6 (e.g. Firefox), non-ES6.
Something like this should work:
function pathsToObject(paths) {
var result = [ ];
// Iterate through the original list, spliting up each path
// and passing it to our recursive processing function
paths.forEach(function(path) {
path = path.split('/');
buildFromSegments(result, path);
});
return result;
// Processes each path recursively, one segment at a time
function buildFromSegments(scope, pathSegments) {
// Remove the first segment from the path
var current = pathSegments.shift();
// See if that segment already exists in the current scope
var found = findInScope(scope, current);
// If we did not find a match, create the new object for
// this path segment
if (! found) {
scope.push(found = {
label: current
});
}
// If there are still path segments left, we need to create
// a children array (if we haven't already) and recurse further
if (pathSegments.length) {
found.children = found.children || [ ];
buildFromSegments(found.children, pathSegments);
}
}
// Attempts to find a ptah segment in the current scope
function findInScope(scope, find) {
for (var i = 0; i < scope.length; i++) {
if (scope[i].label === find) {
return scope[i];
}
}
}
}

Categories

Resources