I have two immutable arrays. One is ordinary ([1,2,3,4]), the other is multiplied by two ([2,4,6,8]).
How in the test to equalize each value of the first array with the value of the second I use the iteration? That 1 is 2, and 2 is 4 and so on.
I think this can be done with a for loop, but I do not know how to write this in practice.
import { List, Set } from "immutable"
export function mass() {
let standardArray = List([1,2,3,4]);
let mutatedArray = standardArray.map(x => x * 2);
return mutatedArray;
};
test code
(I do not know how to proceed)
import { List, Set, isImmutable, Record, Map } from "immutable"
import { mass } from "./sum";
test('Array Multiplication Test', () => {
let standardArray = List([1,2,3,4]);
let mutatedArray = standardArray.map(x => x * 2);
expect(standardArray).not.toEqual(mutatedArray);
});
Why not make use of a flag. Take a flag variable and set it to any value, loop through the array and if any value does not meet expectations, change the flag variable.
In the end, check the value of flag variable. If it is the same as set initially then they are same otherwise the arrays are different.
import { List, Set, isImmutable, Record, Map } from "immutable"
import { mass } from "./sum";
test('Array Multiplication Test', () => {
let standardArray = List([1,2,3,4]);
let mutatedArray = standardArray.map(x => x * 2);
let flag = false;
for (let i = 0; i < standardArray.length; i++) {
if (standardArray[i] * 2 != mutatedArray[i]) {
flag = true;
break;
}
}
expect(flag).toEqual(false);
});
I think you want something like this:
let testArr1 = [1,2,3,4]
let testArr2 = [2,4,6,8]
let testArr3 = [2,6,6,8]
function doesMatchCriteria(arr1, arr2) {
if (arr1.length !== arr2.length) return false;
return arr1.every((e, i) => ((e*2) === arr2[i]));
return true;
}
console.log(doesMatchCriteria(testArr1, testArr2)); // true
console.log(doesMatchCriteria(testArr1, testArr3)); // false
So, your test function could be something like this:
test('Array Multiplication Test', () => {
let testArr1 = [1,2,3,4]
let testArr2 = [2,4,6,8]
expect(doesMatchCriteria(testArr1, testArr2)).toBe(true);
});
Don't forget to define doesMatchCriteria function in the same file.
Related
So I am basically trying to change the variable "status" when I execute the code below.
const Ship = (length) => {
let status = "good"
let array = []
for (let i = 1; i <= length; i++) {
array.push(i)
}
const hit = (number) => {
if (!number) {
return array
}
array[number - 1] = number + 10
status = "bad"
}
return {
length,
hit,
array,
status
}
}
const ships = Ship(2)
console.log(ships.status) //initial status
console.log(ships.array) //initial array
ships.hit(1)
console.log(ships.array) //modified array
console.log(ships.status) //not modified status
It should work,since the array gets modified, but for some reason it doesn't.
I want to know WHY it doesn't work, not a work around.
You declare the hit function but didn't run it,
const hit = (number) => {
if (!number) {
return array
}
array[number - 1] = number + 10
status = "bad"
}
hit(number) <---
You're getting a copy of the status variable in function scope via closure. I would suggest using class semantics for your usecase
class Ship {
constructor(length) {
this.array = []
this.status = "good"
for (let i = 1; i <= length; i++) {
this.array.push(i)
}
}
hit = (number) => {
if (!number) {
return this.array
}
this.array[number - 1] = number + 10
this.status = "bad"
}
}
The last part to this exercise is to write a recursive function that takes two parameters, a joined list and an index respectively. The function will find the value in the object within the list at it's respective index. The code i have written works the way i want (i can see it working when i console.log for every occasion the function is called. But on the last occasion it refers undefined as my value. I cannot understand why. Oh and it works for index of 0. code as followed.
and first, list looks like this:
list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
const nth = (list, targetNum) => {
let value = Object.values(list)[0];
if (targetNum == 0) {
return value;
} else {
targetNum = targetNum -1;
list = Object.values(list)[1];
// console.log(value);
// console.log(targetNum);
// console.log(list);
nth(list, targetNum);
}
};
console.log(nth(arrayToList([1,2,3]),2));
below is the code for arrayToList it was the first part of the exercise and if you have any comments that's cool, cause the hints ended up suggesting to build the list from the end.
const arrayToList = (arr) => {
let list = {
value: arr[0],
rest: nestObject()
};
function nestObject() {
let rest = {};
arr.shift();
const length = arr.length;
if (length == 1) {
rest.value = arr[0];
rest.rest = null;
} else {
rest.value = arr[0];
rest.rest = nestObject();
}
return rest;
}
return list;
};
Both solutions are convoluted and unnecessary verbose. Actually, both functions could be one-liners. Here are a few hints:
For the toList thing consider the following:
if the input array is empty, return null (base case)
otherwise, split the input array into the "head" (=the first element) and "tail" (=the rest). For example, [1,2,3,4] => 1 and [2,3,4]
return an object with value equal to "head" and rest equal to toList applied to the "tail" (recursion)
On a more advanced note, the split can be done right in the function signature with destructuring:
const toList = ([head=null, ...tail]) => ...
Similarly for nth(list, N)
if N is zero, return list.value (base case)
otherwise, return an application of nth with arguments list.rest and N-1 (recursion)
Again, the signature can benefit from destructuring:
const nth = ({value, rest}, n) =>
Full code, if you're interested:
const toList = ([value = null, ...rest]) =>
value === null
? null
: {value, rest: toList(rest)}
const nth = ({value, rest}, n) =>
n === 0
? value
: nth(rest, n - 1)
//
let lst = toList(['a', 'b', 'c', 'd', 'e', 'f'])
// or simply toList('abcdef')
console.log(lst)
console.log(nth(lst, 0))
console.log(nth(lst, 4))
You simply need to add a return when recursively calling nth. Otherwise the logic is carried out but no value is returned (unless targetNum is 0)
const nth = (list, targetNum) => {
let value = Object.values(list)[0];
if (targetNum == 0) {
return value;
} else {
targetNum = targetNum -1;
list = Object.values(list)[1];
return nth(list, targetNum); // return needed here too
}
};
Or more succinctly:
const nth = (list, n) => n === 0 ? list.value : nth(list.rest, n - 1)
Here's another non-recursive arrayToList that builds the list from the end:
const arrayToList = arr => arr.slice().reverse().reduce((rest, value) => ({value, rest}), null);
(The slice here is just to make a copy of the array so that the original is not reversed in place.)
Georg’s recursive solutions are beautiful!
I’d like to add the hinted “build the list from the end” solution from the book:
const arrayToList => (arr) => {
var list
while (arr.length) {
list = {value: arr.pop(), rest: list}
}
return list
}
I want to sort an array values in an ascending or descending order without using sort().
I have created a function, however I am not satisfied with it.
I believe the code below could be much shorter and more concise.
Please let me know where to modify or you may entirely change the code too. Thank you in advance.
const func = arg => {
let flip = false;
let copy = [];
for(let val of arg) copy[copy.length] = val;
for(let i=0; i<arg.length; i++) {
const previous = arg[i-1];
const current = arg[i];
if(previous > current) {
flip = true;
copy[i] = previous;
copy[i-1] = current;
}
}
if(flip) return func(copy);
return copy;
};
l(func([5,2,8,1,9,4,7,3,6]));
If your input is composed of whole numbers, as in the example, pne option is to reduce the array into an object, whose keys are the numbers, and whose values are the number of times those values have occured so far. Then, iterate over the object (whose Object.entries will iterate in ascending numeric key order, for whole number keys), and create the array to return:
const func = arr => {
const valuesObj = {};
arr.forEach((num) => {
valuesObj[num] = (valuesObj[num] || 0) + 1;
});
return Object.entries(valuesObj)
.flatMap(
([num, count]) => Array(count).fill(num)
);
};
console.log(
func([5,2,8,1,9,10,10,11,4,7,3,6])
);
This runs in O(N) time.
To account for negative integers as well while keeping O(N) runtime, create another object for negatives:
const func = arr => {
const valuesObj = {};
const negativeValuesObj = {};
arr.forEach((num) => {
if (num >= 0) valuesObj[num] = (valuesObj[num] || 0) + 1;
else negativeValuesObj[-num] = (negativeValuesObj[-num] || 0) + 1;
});
return [
...Object.entries(negativeValuesObj).reverse()
.flatMap(
([num, count]) => Array(count).fill(-num)
),
...Object.entries(valuesObj)
.flatMap(
([num, count]) => Array(count).fill(num)
)
];
};
console.log(
func([5,2,8,1,-5, -1, 9,10,10,11,4,7,3,6, -10])
);
For non-integer items, you'll have to use a different algorithm with higher computational complexity.
How to write a function to remove certain elements into a new array and leave the original array with only the remaining elements?
the first part is easy using a for loop pushing the even numbers into a new array but mutating the original array to leave only the odd numbers is hard
function remove(arr, cb){
var removed = [];
var newArr = [];
for(var i = 0; i < arr.length; i++) {
if(cb(arr[i], i, arr)) {
removed.push(arr[i]);
}
}
return removed;
}
Use an else statement to fill newArr with values that should stay in the original arr, then empty it using splice() before copying the items from newArr back into it.
function remove (arr, cb) {
var removed = [];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (cb(arr[i], i, arr)) {
removed.push(arr[i]);
} else {
newArr.push(arr[i]);
}
}
arr.splice(0);
for (var i = 0; i < newArr.length; i++) {
arr.push(newArr[i]);
}
return removed;
}
Welcome to Stackoverflow!
Personally, I'd avoid anything that mutates an input parameter, as this increases code complexity and makes it hard to reason about what's happening from the calling side.
Instead, I'd write a method that returns an array of two arrays. This can be easily split into two variables at the calling end using by using array destructuring.
See the example below:
const splitArr = (arr, pred) =>
arr.reduce(
(prev, curr, idx) => {
prev[+pred(curr, idx, arr)].push(curr);
return prev;
}, [[], []]
);
// usage //
const myArr = [1, 2, 3, 4];
const [arr1, arr2] = splitArr(myArr, x => x > 2);
console.log(arr1);
console.log(arr2);
Because pred is a function that returns a boolean value, we can co-erce this value to 0 or 1 using +someBoolean. We can then use this value as an index to decide into which of the two output arrays the value should be pushed.
You were definitely on the right track with your solution, a couple tweaks and we can make it very readable and also very easy to work with. I tried to keep the format of what it looked like you were doing.
I do take advantage of destructuring here, this could be returned as just an object, and then reference the properties.
const myArr = [0,1,2,3,4,5,6,7,8,9,10];
const splitItems = (arr, logicFunc) => {
let secondSet = []
const firstSet = arr.filter(v => {
if (logicFunc(v)) return true
else secondSet.push(v)
})
return { firstSet, secondSet }
}
const myLogicFunc = v => (v < 3 || v == 9)
const { firstSet, secondSet } = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${firstSet}`) // My first set: 0,1,2,9
console.log(`My second set: ${secondSet}`) // My second set: 3,4,5,6,7,8,10
/* OR without destructuring:
const myArrays = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${myArrays.firstSet}`)
console.log(`My second set: ${myArrays.secondSet}`)
*/
Please let me know if you have any questions
In modern JavaScript apps we do not mutate arrays we create new array, this avoids side effects, so what we do is create two new arrays
const split = (source, conditionFunc) = [ source.filter(i => conditionFunc(i)), source.filter(i => !conditionFunc(i))];
Then you have an array of two arrays of the values that meed condition and those that don't and you have not caused any side effects.
const odssAndEvens = split(source, i => i % 2 === 1);
Or with reduce so you don't iterate the array twice
const split = (source, conditionFunc) = source.reduce((results, item) => {
if (conditionFunc(item)) {
results[0].push(item);
} else {
results[1].push(item);
}
return results;
}, [[],[]]);
How in the test to compare each value of the array, and not the entire array?
In my test, I compared the standardArray, but I need to compare List [1,2,3,4], but I'll get it so that the test does not lose its meaning.
Maybe somehow by the indexes or otherwise ...
import { List, Set } from "immutable"
let standardArray = List([1,2,3,4]);
export function mass(standardArray) {
let mutatedArray = standardArray.map(x => x * 2);
return mutatedArray;
};
test code:
import { List, Set, isImmutable, Record, Map } from "immutable"
import { mass } from "./sum";
test('aligning arrays', () => {
let standardArray = List([1,2,3,4]);
for (let i = 0; i < 1; i++) {
expect(mass(standardArray)).toEqual(standardArray.map(x => x * 2));
};
});
If you want to check each value you can try something like this:
test('values should match', () => {
let originalArray = List([1,2,3,4]);
let expectedResult = List([2,4,6,8]);
let actuaResult = mass(originalArray);
for (let i = 0; i < originalArray.length; i++) {
expect(expectedResult[i]).toEqual(actuaResult[i]);
};
});