Say my function is called as,
my_function(1, 2, 5, null, 4, null, 1, 3);
and I want to split it into separate segments that last up until a null.
Here's what I have so far:
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
let j = 0;
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
_tempArray[j].push(theArgs[i]); //breaks here
} else {
_theArgs[j].push(_tempArray); //and here
_tempArray = [];
j++;
}
}
return _theArgs;
}
my_function(1, 2, 5, null, 4, null, 1, 3);
So here I am trying to cycle through each argument passed to the function and split it up into a 2D array. For instance, my_function(1, 2, 5, null, 4, null, 1, 3); would return an array _theArgs where _theArgs[0] = [1, 2, 5], _theArgs[1] = [4] and _theArgs[2] = [1, 3]
I've indicated where my code breaks.. any suggestion to approaching this would be much appreciated
You could search for null and push the parts to the result array.
function fn(...args) {
var result = [],
r = 0,
l;
while ((l = args.indexOf(null, l)) !== -1) {
result.push(args.slice(r, l));
r = l + 1;
l += 2;
}
result.push(args.slice(r));
return result;
}
console.log(fn(1, 2, 5, null, 4, null, 1, 3));
_tempArray[j].push() fails because _tempArray[j] is not an array. _tempArray is initially an empty array, there's nothing in _tempArray[j], so you can't push onto it. I think you just want _tempArray.push(theArgs[i]).
The same with _theArgs[j].
You also need to push onto _theArgs at the end of the function, to get arguments after the last null.
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] !== null) {
_tempArray.push(theArgs[i]);
} else {
_theArgs.push(_tempArray);
_tempArray = [];
}
}
if (_tempArray.length > 0) {
_theArgs.push(_tempArray);
}
return _theArgs;
}
console.log(my_function(1, 2, 5, null, 4, null, 1, 3));
This is what you need: (Since you are pushing you don't need the variable j)
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
_tempArray.push(theArgs[i]); //breaks here
} else {
_theArgs.push(_tempArray); //and here
_tempArray = [];
}
}
return _theArgs;
}
console.log(my_function(1, 2, 5, null, 4, null, 1, 3));
It looks like you're trying to call push() on an undefined nested array object (that is being accessed via the j variable).
Consider making the following changes:
const my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
// Push to _tempArray directly. No need to access via "j"
_tempArray.push(theArgs[i]);
} else {
// Push to _theArgs directly. No need to access via "j"
_theArgs.push(_tempArray);
_tempArray = [];
}
}
// Include last args items (if any after final null)
if(_tempArray.length > 0) {
_theArgs.push(_tempArray);
}
return _theArgs;
}
const result = my_function(1, 2, 5, null, 4, null, 1, 3);
console.log(result)
The j index is throwing you off; remove it in both cases (it's an undefined offset; push will create the new element automatically).
Also, you'll need to append the final _tempArray before returning the result to ensure the last row is added.
Here's a cleaner version which avoids indexes entirely:
const myFunction = (...args) => {
let row = [];
const result = args.reduce((a, e) => {
if (e === null) {
a.push(row);
row = [];
}
else {
row.push(e);
}
return a;
}, []);
return row.length ? result.concat([row]) : result;
}
console.log(myFunction(1, 2, 5, null, 4, null, 1, 3));
Another Solution:
function my_function(...args) {
var i = 0, temp = [], lastElt = null;
for (j = 0; j < args.length; j++) {
if (args[j] !== null) {
if (temp[i] === undefined) {
temp[i] = [];
}
temp[i].push(args[j]);
} else if (lastElt !== null) {
i++;
}
lastElt = args[j];
}
return temp;
}
var test = my_function(null, 1, 2, 5, null, null, 4, null, null, null, 1, 3);
console.log(test);
As covered in earlier answers the problem is caused by processing a second dimension of _theArgs and _tempArray when only one (or the first) dimension should be used.
Removing two occurences of [j] and the j++; line will fix this but needs patch-up code to include an array of arguments that follow the last null argument.
You could use Array.prototype.reduce to avoid explicit loop code and patchups as may be required:
const args2D = (...argList) =>
argList.reduce(
(result, arg) => {
if (arg === null){
result.push( [] );
}
else {
result[ result.length-1].push( arg);
}
return result;
},
[[]]
);
console.log( args2D(1, 2, 5, null, 4, null, 1, 3));
This approach always produces a two dimensional array, with empty inner array(s) if the argument list is empty, or no non-null values follow a null value in the list.
Short and Sweet - not recommended for server-side scripts because its not as performant as a for loop solution but for the client you may save some bandwidth. Also note that this solution, as presented, only works with numbers, nulls and modern browsers.
my_function = (...theArgs) => {
var _theArgs = theArgs.toString().split(',,');
for(arg in _theArgs) {
_theArgs[arg] = _theArgs[arg].split(',').map(function(v) {return +v })
}
return _theArgs;
};
console.log(my_function(1, 2, 5, null, 4, null, 1, 3) );
Related
I was trying to find as a dictionary (JS object) how many times each of the elements appears in some list. For example, the appearance of 1 in the list [1,1,2,2,1,3,4] is 3, because there are three ones in the list. In python I would implement this in the following way:
l = [1,1,2,2,1,3,4]
apr = dict()
for e in l:
if (e in apr.keys()):
apr[e] += 1
else:
apr[e] = 1
In javascript I decided to do
var arr = [1,1,2,2,1,3,4];
var vals = {};
for (let e of arr){
if (e in Object.keys(vals)){
vals[e] = vals[e] + 1;
}
else{
vals[e] = 1;
}
}
// vals = { '1': 2, '2': 1, '3': 1, '4': 1 }
which is obviously wrong. Then I tried if (Object.keys(vals).includes(e)){ instead, and it didn't work either. At the very end I implemented my own function my_in, and the progem worked:
function my_in(val,arr){
for (i = 0; i<=arr.length; i++){
if (val==arr[i]){
return true;
}
}
return false;
}
var arr = [1,1,2,2,1,3,4];
var vals = {};
for (let e of arr){
//if (Object.keys(vals).includes(e)){
// if (e in Object.keys(vals)){
if (my_in(e, Object.keys(vals))) {
vals[e] = vals[e] + 1;
}
else{
vals[e] = 1;
}
}
console.log(vals);
But I am still confused why did the first two tries didn't work. Is there anything I should be aware of when using in or includes()?
You need to use in operator with the object, not with an array this would check the indices.
For example by using an an array of keys
vals = { 1: 1 }
keys = Object(vals) // ['1']
if (1 in keys) performs
-> 1 in { 0: 1, length: 1 }, because there is only index zero with value one
-> false
var arr = [1, 1, 2, 2, 1, 3, 4];
var vals = {};
for (let e of arr) {
if (e in vals) { // check with object
vals[e] = vals[e] + 1;
} else {
vals[e] = 1;
}
}
console.log(vals);
The Object.keys(object) function returns an array of the string keys in the object.
When you do if (e in Object.keys(vals)) this would actually check the e element in the array returned from the Object.keys call. Which is not what you want.
You should simply check the presence of the e key in the vals object, if present increment by 1 else assign 0 to it:
var arr = [1,1,2,2,1,3,4];
var vals = {};
for (let e of arr){
vals[e] = (vals[e] || 0) + 1;
}
console.log(vals)
The issue with your code was, when you did Object.keys(vals) for the first iteration it returned an empty array [] as vals was an empty object {}. So e in Object.keys(vals) was false and it went to the else block. In the else block the vals[e] = 1 was executed.
In the next iteration when the check e in Object.keys(vals) happened it was evaluated to true as the array ["1"] was returned because vals is {1: 1}, same for the other elements so that is why you see {1: 2, 2: 1...}.
I'm trying to create an algorithm to find duplicate values in a list and return their respective indexes, but the script only returns the correct value, when I have 2 equal elements:
array = [1,2,0,5,0]
result -> (2) [2,4]
Like the example below:
array = [0,0,2,7,0];
result -> (6) [0, 1, 0, 1, 0, 4]
The expected result would be [0,1,4]
Current code:
const numbers = [1,2,0,5,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(numbers.indexOf(avg),numbers.indexOf(avg,n_loop))
};
};
};
return tie;
}
console.log(checkATie(numbers));
if possible I would like to know some way to make this code more concise and simple
Use a Set
return [...new Set(tie)]
const numbers1 = [1,2,0,5,0];
const numbers2 = [0,0,2,7,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(avgList.indexOf(avg),avgList.indexOf(avg,n_loop))
};
};
};
return [...new Set(tie)]
}
console.log(checkATie(numbers1));
console.log(checkATie(numbers2));
I hope this help you.you can use foreach function to check each item of array
var array = [0,0,2,7,0];
var result = [] ;
array.forEach((item , index)=>{
if(array.findIndex((el , i )=> item === el && index !== i ) > -1 ){
result.push(index)
}
})
console.log(result);
//duplicate entries as an object
checkDuplicateEntries = (array) => {
const duplicates = {};
for (let i = 0; i < array.length; i++) {
if (duplicates.hasOwnProperty(array[i])) {
duplicates[array[i]].push(i);
} else if (array.lastIndexOf(array[i]) !== i) {
duplicates[array[i]] = [i];
}
}
console.log(duplicates);
}
checkDuplicateEntries([1,2,0,5,0]);
// hope this will help
Create a lookup object with value and their indexes and then filter all the values which occurred more than once and then merge all indexes and generate a new array.
const array = [1, 2, 0, 5, 0, 1, 0, 2],
result = Object.values(array.reduce((r, v, i) => {
r[v] = r[v] || [];
r[v].push(i);
return r;
}, {}))
.filter((indexes) => indexes.length > 1)
.flatMap(x => x);
console.log(result);
class LinkedListNode {
constructor(value) {
this.value = value;
this.next = null;
}
}
let head = new LinkedListNode("head");
let x = [1, 1, 1, 1, 4, 10, 10, 3, 10, 9, 5, 5, 5, 8];
for (let ele of x) {
let y = new LinkedListNode(ele);
let pointer = head;
while (pointer.next != null) {
pointer = pointer.next;
}
pointer.next = y;
}
Can someone explain why the following 'solution' leads to an infinite loop?
let removeDup = function(sll) {
let array = []
let pointer = sll;
while (pointer) {
if (array.includes(pointer.value)){
} else {
array.push(pointer.value);
sll.next = pointer;
sll = sll.next;
}
pointer = pointer.next;
}
}
It appears that if I
let pointer = sll.next;
or
let array = [sll.value]
then it works fine but if I run it as is then it leads to an infinite loop. I can see why it may make a linked list with 2 duplicates of the first value but I can't understand why it makes an infinite loop. Alternatively, if anyone can point me in the right direction for debugging this then that would be appreciated also!
Looks like you end up defining a node that references itself within your else condition.
You may be looking for something like this:
class LinkedListNode {
constructor(value) {
this.value = value;
this.next = null;
}
}
let head = new LinkedListNode("head");
let x = [1, 1, 1, 1, 4, 10, 10, 3, 10, 9, 5, 5, 5, 8];
for (let ele of x) {
let y = new LinkedListNode(ele);
let pointer = head;
while (pointer.next != null) {
pointer = pointer.next;
}
pointer.next = y;
}
function removeDup(currentNode = sll) {
const seen = {};
let lastUnique;
while (currentNode) {
if (seen[currentNode.value]) {
lastUnique.next = currentNode.next;
} else {
seen[currentNode.value] = true;
lastUnique = currentNode;
}
currentNode = currentNode.next;
}
}
removeDup(head);
let outputNode = head;
while (outputNode) {
outputNode = outputNode.next;
if (outputNode) {
console.log(outputNode.value);
}
}
How to investigate your bug
You can use a debugger, but for primitive people like me, you can also use console.log
In case of infinite loop, what you want is to break free
let count = 0
while (pointer && count++<5) {
//whatever
}
Even if you fail in your algorithm you will still exit
Output your variable
Be creative and spam console.log wherever you see fit. put some string to remind you what junk you outputed
while (pointer) {
if (array.includes(pointer.value)){
console.log('cached skip')
} else {
console.log('pointervalue', pointer.value)
array.push(pointer.value);
sll.next = pointer;
sll = sll.next;
console.log('sllendloop', sll)
}
pointer = pointer.next;
}
Fixing your code
Note: do not use array for cache because it is O(n) look
You may use a Set (O(1)) instead
const removeDup = function(sll) {
const dupes = new Set()
let cur = { next: null }
// you can also as you suggested initialize cur as sll
// in which case you must mark it as "consumed" idem add the value to dupes
let sllIter = sll
while (sllIter) {
if (dupes.has(sllIter.value)) {
// early continue to avoid nesting else
// subjective preference
sllIter = sllIter.next
continue
}
dupes.add(sllIter.value)
// link our node to the currently being iterated node
cur.next = sllIter;
// advance our node
cur = sllIter
// advance iter
sllIter = sllIter.next
}
return sll
}
const l = (value, next) => ({ value, next })
const sllStr = ({ value: a, next: b }) => b ? `${a} -> ${sllStr(b)}`: a
const sll = l(1, l(1, l(2, l(1, l(2, l(3))))))
console.log(sllStr(removeDup(sll))) // 1 -> 2 -> 3
You may write a micro Linked List library perhaps. Here is one with function descriptions;
toList : takes an array converts it into a list
foldList : Something like reduce. Takes a list, a function and an accumulator.
sum : takes a list and gives it's sum :)
prt : pretty print a list
nub : remove dupes from a list.
function List(e){
this.data = e;
this.next = null;
}
List.fromArray = function([a,...as]){
var h = new List(a),
t = as.reduce((l,e) => [l[0],l[1].next = new List(e)], [h,h]);
return t[0];
};
List.prototype.fold = function (f,a){
var newa = f(a, this.data);
return this.next ? this.next.fold(f, newa)
: newa
};
List.prototype.log = function(){
return this.fold((a,e) => a + e + " -> ", "") + "null";
};
List.prototype.nub = function(){
var o = this.fold((a,e) => (a[e] = e, a), {}),
a = Object.values(o);
return List.fromArray(a);
}
var arr = [1, 1, 1, 1, 4, 10, 10, 3, 10, 9, 5, 5, 5, 8],
lst = List.fromArray(arr),
sum = l => l.fold((a,e) => a + e, 0), // just for fun
res = lst.nub().log();
console.log(res);
nub is O(n).
If I call the filter function, I get this array returned [ 1, , 3, , 5 ]. From where come the additional commas? I don't understand this effect. Can somebody explain it to me?
The array should be that: [ 1, 3, 5 ].
class List {
constructor(values = []) {
this._list = values;
}
filter(func) {
let newList = new Array();
let indexList = 0;
let indexNewList = 0;
while (this._list[indexList] != undefined) {
if (func(this._list[indexList]) === true) {
newList[indexNewList] = this._list[indexList];
indexNewList++;
}
indexList++;
}
this._list = newList;
return this;
}
get values() { return this._list }
}
var isOdd = function (x) {
return x % 2 === 1;
};
var list = new List([1, 2, 3, 4, 5]);
console.log(list.filter(isOdd).values);
If an item in the list matches the filter, you're inserting it into the new list at the index of the item in the original list. You want to simply be appending the item to the new list.
Use another variable to keep track of what index the element should be inserted into the new list at:
let newList = new Array();
let indexList = 0;
let newIndex = 0;
while (this._list[indexList] != undefined) {
if (func(this._list[indexList]) === true) {
newList[newIndex] = this._list[indexList];
newIndex++;
}
indexList++;
}
The newIndex variable will only be incremented when an item has been inserted into newList, instead of being incremented with every iteration of the loop.
The problem is the increment of the variable index, that increment is creating empty/undefined elements.
For example:
Array = [1];
index = 1
callback returns false
The index is incremented by 1 -> index =2`
Next iteration callback returns true
A new element is added to Array at position 2 ->
Array = [1, undefined, 3].
Use a separated index for the newArray.
class List {
constructor(values = []) {
this._list = values;
}
filter(func) {
let newList = new Array();
let index = 0;
let newListIndex = 0;
while (this._list[index] != undefined) {
if (func(this._list[index]) === true) {
newList[newListIndex++] = (this._list[index]);
}
index++;
}
this._list = newList;
return this;
}
get values() {
return this._list
}
}
var isOdd = function(x) {
return x % 2 === 1;
};
var list = new List([1, 2, 3, 4, 5]);
console.log(list.filter(isOdd));
I would suggest sticking with a functional-programming style definition of array#filter since that is the paradigm it originates from.
This is the fully immutable version of List#filter where you get a new instance of List back and the underlying array never goes through any form of mutation.
class List {
constructor(values = []) {
this._list = values;
}
filter(func) {
var reduce = function(values, accum) {
if(values.length === 0) return accum;
if(func(values[0])) return reduce(values.slice(1), accum.concat(values[0]));
else return reduce(values.slice(1), accum)
}
return new List(reduce(this._list, []))
}
get values() {
return this._list
}
}
var isOdd = function(x) {
return x % 2 === 1;
};
var list = new List([1, 2, 3, 4, 5]);
console.log(list.filter(isOdd));
I wish to sort an array of medals. My first sort returns an array sorted according to the gold medals. I then wish to range those which are having the same gold but silver medals are different (same for bronze). I use the following codes that actually makes me run out of memory. This is my code:
static sort(data) {
let sorted = data.sort((a, b) => b.medal.gold - a.medal.gold);
let next, temp, current;
for (let i = 0; i < sorted.length; i++) {
current = sorted[i].medal;
if (sorted[i+1]) next = sorted[i+1].medal;
if (next) {
if (current.gold === next.gold) {
if (current.silver < next.silver) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
else if (current.silver === next.silver) {
if (current.bronze < next.bronze) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
}
}
}
}
return sorted;
}
You'll want to improve your compare function so it takes care of that requirement:
data.sort((a, b) => (b.medal.gold - a.medal.gold)
|| (b.medal.silver - a.medal.silver)
|| (b.medal.bronze - a.medal.bronze) )
And then you don't need the (endless) for loop at all.
You have to set next to null somewhere, because it keeps the value from the previous iteration and the if(next) is always true. Afterwards the function will always create one more element and add it in the array (sorted[i+1] = sorted[i]) until you run out of memory.
Here is a working example:
var rawData =
[{ id: 1, medal: {gold: 2, silver: 1, bronze: 1}},
{ id: 2, medal: {gold: 2, silver: 1, bronze: 2} },
{ id: 3, medal: {gold: 5, silver: 1, bronze: 4} } ];
function sortData(data) {
let sorted = data.sort((a, b) => b.medal.gold - a.medal.gold);
let next, temp, current;
for (let i = 0; i < sorted.length; i++) {
next = undefined;
current = sorted[i].medal;
if (sorted[i+1]) next = sorted[i+1].medal;
if (next) {
if (current.gold === next.gold) {
if (current.silver < next.silver) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
else if (current.silver === next.silver) {
if (current.bronze < next.bronze) {
temp = sorted[i+1];
sorted[i+1] = sorted[i];
sorted[i] = temp;
}
}
}
}
}
return sorted;
};
console.log(sortData(rawData))
Please note that in the function you are using medal instead of medals as the data you have provided in one of your comments.