How to iterate through a linked list in javascript - javascript

Someone shared this beautifully elegant way to create a linked list from an array.
function removeKFromList(l, k) {
let list = l.reduceRight((value, next)=>({next, value}), null);
console.log(list);
}
let l = [3, 1, 2, 3, 4, 5];
let k = 3;
removeKFromList(l, k);
It's pretty easy to iterate arrays (for, filter, map, reduce, etc.) but a linked list has none of those features. I need to iterate through to remove k values from the list. I'm guessing I need to create a variable for the current node and use a while loop, but there's no documentation on this. I've seen some repl code doing it but it seems unnecessarily complicated.
How do you iterate through a linked list in javascript?

First of all, although the idea of using reduce is indeed beautiful, I must say that the result is not so good, because the resulting nodes have a field "next" where the value is and a field "value" where the next is, i.e. they are swapped. So let's fix that:
function removeKFromList(l, k) {
let list = l.reduceRight((value, next)=>({value: next, next: value}), null);
console.log(list);
}
Secondly, the name of that function is awful, it should be named "arrayToLinkedList" or something more suggestive. Also, logging the result does not make sense, we should return it instead. Moreover, the parameter k is simply unused. Fixing those things:
function arrayToLinkedList(array) {
return array.reduceRight((prev, cur) => ({ value: cur, next: prev }), null);
}
Now, let's work on how to iterate over this. What do you think? I will give some hints because giving the answer directly probably won't help you learn much:
Observe that the linked list itself is:
Either the value null, or
A plain object with two fields, one field "value" which can be anything, and one field "next" which is a linked list.
Observe that the above (recursive) definition is well-defined and covers all cases.
Now, use that to your assistence. How do you get the first value? That would be simply myList.value, right? Now how do I get the second value? By applying .next, we get the next linked list, and so on. If next is null, you know you have to stop.
Let me know if you need further assistance.
EDIT: I noticed that you're looking for the right way to make an iterating method on lists with an idea of "adding it to the prototype" or something. So, about that:
To add an instance method by a prototype, you'd need your linked lists to be a class. That would be overcomplicating, unless you really have a good reason to define a class for this (which would be creating several methods and utility things around it).
It is much better, in my opinion, to just define a function that takes one linked list as the first parameter and a callback function as the second parameter, similarly to lodash's .each:
function forEachValueInLinkedList(linkedList, callback) {
// here you loop through all values and call
// callback(value)
// for each value.
}
I'd say this would be the most "javascriptonic" way to do it.

Related

Array of Objects Compare and Exclude

I am using this handy function:
function not(a, b) {
return a.filter((value) => b.indexOf(value) === -1);
}
Then I have this
let AvailableList = not(FullList, MyList);
If I were to give example of first element in MyList and FullList it is:
appName: "MyAppName*"
description: null
permissionID: MyAppID*
permissionName: "MyAppPermName*"
__proto__: Object
So initially AvailableList is full of items like above. When user makes selection and saves it I save it into MyList. So then I need to use this not function to calculate remaining parts from MyList. But even when I move the first item of AvailableList to MyList then check like if(AvailableList === MyList) console.log(true) else console.log(false) I get false. But if I console log both of the array's first items they print identical values. I tried to compare after declaring variables using Object.assign() as well but no help.
Perhaps I can tackle this easily checking a value like permissionID, but I do not want to go that route since there will be other items later on that won't have permissionID.
If you know of a different function that can do a better job, feel free to share. Please don't share library solutions like Lodash.
*entries are masked

How to update an array after splice in Svelte?

I'm learning Svelte, and read in the documentation that arrays need to be reassigned in order for a component or page to update it. For that they devised a more idiomatic solution. Instead of writing:
messages.push('hello');
messages = messages;
you can write instead:
messages = [...messages, 'hello'];
Alright, makes sense. But then the documentation says:
You can use similar patterns to replace pop, shift, unshift and splice.
But how? I cannot see how you can remove items from an array. More to the point, how could I write the following more idiomatically?
messages.splice(messages.indexOf('hello'), 1);
messages = messages;
You could e.g. use the filter array method to create a new array without the element 'hello':
messages = messages.filter(m => m !== 'hello');
As mentioned, Svelte's reactivity is triggered by assignments. The current Svelte tutorial uses JavaScript's (ES6) spread syntax (three dots) to add the next-higher number to an array, providing a more idiomatic solution than a redundant assignment using push:
function pushNumber() {
numbers = [...numbers, lastnumber]; // 1, 2, 3, 4, 5
}
You could use spread syntax to replace pop, shift, unshift and splicethough it might increase the time and complexity of the operation in some cases:
function unshiftNumber() {
numbers = [firstnumber, ...numbers]; // 0, 1, 2, 3, 4
}
function popNumber() {
numbers = [...numbers.slice(0,numbers.length - 1)]; // 1, 2, 3
}
function shiftNumber() {
numbers = [...numbers.slice(1,numbers.length)]; // 2, 3, 4
}
function spliceNumber() {
numbers = [firstnumber, ...numbers.slice(0,numbers.length-1)];// 0, 1, 2, 3
}
Spread is just one way to do it, though. The purpose behind not using pop/push etc is to encourage immutability. So any removal can just be a filter, for example.
There are several things to consider here.
Given this code:
messages.splice(messages.indexOf('hello'), 1);
messages = messages;
What's happening here is:
Looking for the first occurrence of the string "hello" in the array
Removing such element from the array, based on the index found.
The assumption here is that "hello" needs to exists, otherwise the could would remove the last item from the array (since indexOf returns -1).
The original array is therefore mutate: depends by the context, that sometimes can be preferable instead of copying the whole array into a new one; otherwise it's generally a better practice avoid such mutation.
So. If you want to have this behavior exactly, probably this is the best code you can have. For example, takes the filter example:
messages = messages.filter(message => message !== "hello")
What's happening here is:
Filter out any element equals to "hello"
Returns a new array without such element
So it's quite different from the original code: first of all, it always loop the whole array. If you have thousands of element, even if you have only one "hello" at the second index, it would always iterate all of them. Maybe it's what you want, maybe not. If the element is unique, such as an id, maybe you want to stop once you find it.
Second, it returns a new array. Again, that usually a better practice than mutate the array, but in some context it's preferable mutate it instead of create a new one.
So, if you want to mutate the original array, it's probably better to stick to your original code.
If, instead, you don't care (such as the example of push), I believe that in the intention of svelte's developers, your code would be roughly translate to:
let i = messages.indexOf("hello");
messages = [...messages.slice(0, i), ...messages.slice(i + 1)];
(Still assuming there is a "hello" message and you're interested only in the first occurrence).
It's unfortunate that JS doesn't have a better syntax to handles slices.
In case you're wandering, filter can also be used to remove elements using a given index:
let elements = ['a','b', 'c'];
let idx = 1;
elements = elements.filter( (e,i) => i !== idx );
// => ['a', 'c']
You can perform the usual push and pop or `splice on your Array
But because Svelte's reactivity is triggered by assignments, using array methods like push and splice won't automatically cause updates.
According to All about Immutable Arrays and Objects in JavaScript you can do it this way...
let messages = ['something', 'another', 'hello', 'word', 'another', 'again'];
const indexOfHello = messages.indexOf('hello');
messages = [...messages.slice(0, indexOfHello), ...messages.slice(indexOfHello + 1)];
Note the difference between splice and slice
The splice() method adds/removes items to/from an array, and returns
the removed item(s). Note: This method changes the original array.
Syntax: array.splice(start, deleteCount, itemstoAdd, addThisToo);
But
The slice() method returns the selected elements in an array, as a new array object. The slice() method selects the elements starting at the given start argument, and ends at, but does not include, the given end argument.
Note: The original array will not be changed.
In order words
It return a shallow copy of a portion of an array into a new array
object selected from begin to end (end not included). The original
array will not be modified.
Syntax: array.slice(start, end);
You can try this: https://svelte.dev/repl/0dedb37665014ba99e05415a6107bc21?version=3.53.1
use a library called svelox. It allows you to use the Array native api(push/splice...etc.) without reassignment statements.
Spread the spliced array to reassign it to itself ;)
messages = [...messages.splice(messages.indexOf('hello'), 1)];
The goal is to make Svelte detecting that array messages (a property of your component or a variable in the Svelte store) has changed. This is why the array messages must be declared with let or var keyword, not const. This way you're allowed to reassign it. And the reassign operation itself is sufficient to make Svelte detecting that the array has changed.
Perhaps even, simply by doing so works too:
messages = messages.splice(messages.indexOf('hello'), 1);

Array .filter and .map OR .map with a if condition

Recently I was doing some sort of filter to an array and I came up with two cases and I don't know which one to use.
In terms of performance, which is better to use.
arr.filter(someCondition).map(x => x.something)
Or
arr.map(x => {
if(someCondition) {
return x.something
}
})
One thing to notice is that I'm using react, so have undefined values in the returning array (don't return any thing inside .map), it's totally acceptable.
This involves a lot of questions like, how many elements you have in the array and how many you of then will pass the condition and that is what make be wonder which one is better.
So, considering n elements and cases where all elements pass the condition and also that no elements pass the condition, which one have better performance?
.filter().map() OR .map with if inside?
First: It's really unlikely to matter.
But: Only because it's okay in your case to have undefined in the result array, the second one will be faster.
Let's compare:
The first way, filter has to make a pass through the entire array, creating a new array with the accepted entries, then map has to make a pass through the new array and make a new array of the something properties.
The second way, map makes one pass through the array, creating a new array with the something properties (or undefined for the ones that would have been filtered out).
Of course, this assumes you aren't just offloading the burden elsewhere (e.g., if React has to do something to ignore those undefineds).
But again: It's unlikely to matter.
I would probably use a third option: Build the result array yourself:
const result = [];
for (const x of arr) {
if(someCondition) {
result[result.length] = x.something;
}
}
That still makes just one pass, but doesn't produce an array with undefined in it. (You can also shoehorn that into a reduce, but it doesn't buy you anything but complexity.)
(Don't worry about for-of requiring an iterator. It gets optimized away in "hot" code.)
You could use reduce function instead of map and filter and it wouldn't return you undefined like when you use map and if.
arr.reduce((acc, x) => (someCondition ? [...acc, x.something] : acc), [])
or
arr.reduce((acc, x) => {
if (someCondition) {
acc.push(x.something);
return acc;
} else return acc;
}, []);
As #briosheje said smaller array would be a plus. As it reduces number of rerendering in your React app where you use this array and it is unnecessary to pass undefined. Reduce function would be much more efficient, I would say.
If you are wondering why I have written 1st one using spread operator and 2nd one without it is because the execution time taken for 1st one is more compared to 2nd one. And that is due to spread operator as it is cloning 'acc'. So if you want lesser execution time go for 2nd one or else if you want lesser LOC go for 1st one

Redefine array using itself

I wrote this solution to a problem I had, but I don't feel entirely comfortable with it. It seems to be dangerous, or at least in bad practice to use an array to redefine itself, sort of like trying to define a word but using the word in the definition. Can anyone explain either why this is wrong, or why it's ok?
let array = []
// other stuff happens to fill the array
array = array.filter(element => element !== true)
The reason I'm doing it this way is that I need that same variable name (array, for these purposes) to remain consistent throughout the function. The contents of the array might be added or removed multiple times before the array gets returned, depending on other behavior happening around it.
Let me know if this is too vague, I'll try to clarify. Thanks!
It's perfectly fine. The right side (array.filter(element => element !== true)) of the assignment will be evaluated first, generate a completely new array and only then it'll be assigned back into array.
You can loop through your array backwards and remove the items, as to not allocate another array and reassign.
for (i = array.length - 1; i >= 0; --i) {
if (!!array[i]) {
array.splice(i, 1);
}
}
This solution is:
Fast
Memory efficient
Will not interfere with any other code that has a reference to your original array.

What's the first argument of Array.apply in JavaScript?

In an answer, #Tushar suggested the syntax corresponding to the following.
Array.apply(null, Array(3).fill(10))
.map(function (item, index) {
return item + index;
});
I do understand what's going on here and I'm satisfied. However, it bugs me a bit that there's this null valued argument doing seemingly nothing. So I went off and started researching. According to the wisdom on the web, it's a reference to this. Now, that gave me very little clarity and despite of putting in cucumbers, arrays, and objects into that, it didn't affect jack, as far I could tell. In fact, I'm curious why the following wouldn't be equivalent, let alone suffice.
Array(3).fill(10)
.map(function (item, index) {
return item + index;
});
Further on, I read something about Cr an IE not accepting array-like objects, which tells me even less. Also, it's a bit hard to verify the age of the article so the validity of its claim's hard to assess. I got as far as to the talk about prototype constructors and gave up, not being sure if I'm on the right path.
The first argument of apply is only important if you're using a function that uses this.
Running the following snippet should make it a little clearer:
var o = {value: 1};
var fn = function(a) {
console.log(a + this.value);
}
value = "something else";
fn("an argument ");
fn.apply(o, [20]);
// the above prints:
// an argument something else
// 21
https://jsfiddle.net/f2zw8edd/
Array(number) creates an array of the given size, however it does not populate any of its data points. Calling map or fill or pretty much anything else on it therefore won't do anything useful.
Array(item1, item2, item...) on the other hand creates an array with the given items as elements of the array. You can call map, fill, whatever you want on this and it will work.
So how can you use this to create an array of a given size that you can call map on? Exactly the way you see.
What Array.apply(null,Array(3)) does is create an unpopulated array of size 3, then passes those 3 undefined items as arguments to Array, thus resulting in
Array(undefined,undefined,undefined), which gives you a mappable array. It's important to note that you're creating two arrays here, but the Array(3) is discarded after use.
So why null? That would be because when creating an array, there is no place where this is relevant. Since the context is irrelevant, you can pass literally anything you want as the context and it will run just fine, but it's easiest to understand that context doesn't matter if you pass it null.

Categories