Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(15) New exercise | Total Integers #443

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
8 changes: 8 additions & 0 deletions 15_totalIntegers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Exercise 15 - totalIntegers

Write a function that takes in an arbitrarily deep array or object and returns the total number of integers stored inside this array or object.

```javascript
totalIntegers([[[5], 3], 0, 2, ['foo'], [], [4, [5, 6]]]); // returns 7
totalIntegers({ a: 1, b: { a: [5, 10], b: 11 } }); // returns 4
```
21 changes: 21 additions & 0 deletions 15_totalIntegers/solution/totalIntegers-solution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const totalIntegers = function (obj, count = 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With count not actually being passed to any recursive calls, I think it's better to just define it in the function body with a let count = 0 somewhere.

const isObject = (value) => typeof value === 'object' && value !== null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helper definition should be moved outside of the recursive function's body. This is because every time this function is run, there must be a new isObject() function object created for the current scope. This is wasteful -- each recursive call could just be using the same isObject() helper function if it were defined outside this function body.

This isn't super meaningful in the context of this specific problem (ie none of the examples are going to have performance issues because of this), but it is good practice to avoid defining constant factors (like this helper function) inside of loops or recursive functions.


if (typeof obj !== 'object' || obj === null) {
nikitarevenco marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to me this is just checking that the object is not an object. Using your helper for this feels cleaner

Suggested change
if (typeof obj !== 'object' || obj === null) {
if (!isObject(obj)) {

return;
}

const elements = Object.values(obj);

for (const el of elements) {
if (Number.isInteger(el)) {
count++;
} else if (isObject(el)) {
count += totalIntegers(el);
}
}
return count;
};

// Do not edit below this line
module.exports = totalIntegers;
40 changes: 40 additions & 0 deletions 15_totalIntegers/solution/totalIntegers-solution.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const totalIntegers = require('./totalIntegers-solution');

describe('totalIntegers', () => {
nikitarevenco marked this conversation as resolved.
Show resolved Hide resolved
test('Works with a simple array of numbers', () => {
expect(totalIntegers([1, 2, 3])).toBe(3);
});
test('Ignores non-integer values', () => {
expect(totalIntegers([1, 2, '3', 4])).toBe(3);
});
test('Works with simple objects', () => {
expect(totalIntegers({ a: 1, b: '2', c: 3 })).toBe(2);
});
test('Works with an empty nested array', () => {
expect(totalIntegers([[], [], []])).toBe(0);
});
test('Works with a very nested array', () => {
expect(totalIntegers([[[[[[[[[[[[[[4]]]]]], 246]]]]]]]])).toBe(2);
});
test('Works with negative numbers', () => {
expect(totalIntegers([5, 7, -7, [45, -1, -0], [4, 7, -4, -4, -4, [777777, -45674]], [-5477654]])).toBe(14);
});
test('Works with floats', () => {
expect(totalIntegers([5, 7.7, 7, [45, 1, 0], [4.0, 7, [7.77777, 4567.4]], [5477.654]])).toBe(7);
});
test('Only accepts arrays or objects', () => {
expect(totalIntegers('2')).toBe(undefined);
expect(totalIntegers(() => {})).toBe(undefined);
expect(totalIntegers(42)).toBe(undefined);
expect(totalIntegers(NaN)).toBe(undefined);
});
test('Works with NaN', () => {
expect(totalIntegers([5, NaN, [NaN, NaN, 64], 4])).toBe(3);
});
test('Works with a nested array of all kinds of things', () => {
expect(totalIntegers([NaN, [[{}], 555 ], '444', [], 74.0, undefined, [[() => {}], [4], Infinity, [[[], -44.0], [null, '-4'], NaN [[]], 6]], () => {}, [[], [-Infinity, ['4'], [4.7, -46.7], NaN]]])).toBe(5);
});
test('Works with arrays containing objects containing integers', () => {
expect(totalIntegers([4, 6, { a: 1, b: { a: [5, 10], b: 11 } }, 9])).toBe(7);
});
});
6 changes: 6 additions & 0 deletions 15_totalIntegers/totalIntegers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const totalIntegers = function() {

};

// Do not edit below this line
module.exports = totalIntegers;
15 changes: 15 additions & 0 deletions 15_totalIntegers/totalIntegers.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const totalIntegers = require('./totalIntegers');

describe('totalIntegers', () => {
test('First test description', () => {
// Replace this comment with any other necessary code, and update the expect line as necessary

expect(totalIntegers()).toBe('');
});

test.skip('Second test description', () => {
// Replace this comment with any other necessary code, and update the expect line as necessary

expect(totalIntegers()).toBe('');
});
});