// this code should fail the tests // it does function totalAgeYoungerThan0(people, age) { let totalAge = 0; for ( let i=0; i in people; i++ ) // this will just always return 0 with the arguments we send if ( people[i].age < age ) totalAge += people[i].age; return totalAge; } // this code should fail the tests // it does function totalAgeYoungerThan1(people,tooOld) { let totalAge = 0; for ( const {age} of people ) // this will throw because our argument isn't iterable if ( age < tooOld ) totalAge += age; return totalAge; } // this code should fail the tests // it does function totalAgeYoungerThan2(xs, age) { const [person,...persons] = xs; // this will throw on not iterable if ( person===undefined ) return 0; else if ( person.age < age ) return totalAgeYoungerThan2(persons,age) + person.age; else return totalAgeYoungerThan2(persons,age); } // this code should theoretically pass the tests // it does // it includes the word `for` but not in a for loop function totalAgeYoungerThan3(people, age) { return people .filter(person => person.age < age) .reduce((formula, person) => formula + person.age, 0) } // passes function totalAgeYoungerThan4(people, age) { return people .filter(person => person.age < age) .reduce((sum, person) => sum + person.age, 0) } // fails function totalAgeYoungerThan5(people, age) { let totalAge = 0; for ( let i=0; i < people.length; i++ ) if ( people[i].age < age ) totalAge += people[i].age; return totalAge; }
- // this code should fail the tests // it does
- function totalAgeYoungerThan0(people, age) {
- let totalAge = 0;
- for ( let i=0; i in people; i++ ) // this will just always return 0 with the arguments we send
- if ( people[i].age < age )
- totalAge += people[i].age;
- return totalAge;
- }
- // this code should fail the tests // it does
- function totalAgeYoungerThan1(people,tooOld) {
- let totalAge = 0;
- for ( const {age} of people ) // this will throw because our argument isn't iterable
- if ( age < tooOld )
- totalAge += age;
- return totalAge;
- }
- // this code should fail the tests // it does
- function totalAgeYoungerThan2(xs, age) {
- const [person,...persons] = xs; // this will throw on not iterable
- if ( person===undefined )
- return 0;
- else if ( person.age < age )
- return totalAgeYoungerThan2(persons,age) + person.age;
- else
- return totalAgeYoungerThan2(persons,age);
- }
- // this code should theoretically pass the tests // it does
- // it includes the word `for` but not in a for loop
- function totalAgeYoungerThan3(people, age) {
- return people
- .filter(person => person.age < age)
- .reduce((formula, person) => formula + person.age, 0)
- }
- // passes
- function totalAgeYoungerThan4(people, age) {
- return people
- .filter(person => person.age < age)
- .reduce((sum, person) => sum + person.age, 0)
- }
- // fails
- function totalAgeYoungerThan5(people, age) {
- let totalAge = 0;
- for ( let i=0; i < people.length; i++ )
- if ( people[i].age < age )
- totalAge += people[i].age;
- return totalAge;
- }
const chai = require('chai'); const expect = chai.expect; chai.config.truncateThreshold = 0; const solution = (people, age) => people.filter(person => person.age < age).reduce((sum, person) => sum + person.age, 0); const reduce = target => (fn,z) => target.reduce(fn,z) ; const map = function map(fn) { return this.reduce( (z,x,i,xs) => [ ...z, fn(x,i,xs) ] , [] ); } ; // `this` requires `function` syntax const filter = function(pred) { return this.reduce( (z,x,i,xs) => pred(x,i,xs) ? [ ...z, x ] : z , [] ); } ; // idem function reducible(target) { return Object.freeze({ map, reduce: reduce(target.map(el => Object.freeze(el))), filter}); } const people = reducible([ {age: 10, hairColor: "brown"}, {age: 30, hairColor: "red"}, {age: 20, hairColor: "brown"}, {age: 40, hairColor: "black"}, ]); const empty = reducible([]); // const totalAgeYoungerThan = totalAgeYoungerThan0; // const totalAgeYoungerThan = totalAgeYoungerThan1; // const totalAgeYoungerThan = totalAgeYoungerThan2; // const totalAgeYoungerThan = totalAgeYoungerThan3; const totalAgeYoungerThan = totalAgeYoungerThan4; // const totalAgeYoungerThan = totalAgeYoungerThan5; describe("totalAgeYoungerThan", () => { describe("totalAgeYoungerThan",() => { it("returns the correct answer", () => { expect(totalAgeYoungerThan(people, 30)).to.deep.eq(30); }) it("returns 0 when there are no people", () => { expect(totalAgeYoungerThan(empty, 30)).to.deep.eq(0); }) it("returns 0 when there are no people who meet the criteria", () => { expect(totalAgeYoungerThan(people, -1)).to.deep.eq(0); }) }); describe("random tests", () => { const dataSets = []; for(let i = 0; i < 0; i++) { const hairColors = ["brown", "black", "blue", "red", "yellow", "orange"]; const people = []; const numberOfPeople = Math.random() * 20 + 5; for(let j = 0; j < numberOfPeople; j++) { people.push({age: j, hairColor: hairColors[Math.floor(Math.random() * hairColors.length)]}); } dataSets.push(reducible(people)); // generates custom object instead of array } describe("totalAgeYoungerThan", () => { dataSets.forEach((people, i) => { const randomAges = []; for(let j = 0; j < 3; j++) randomAges.push(people[Math.floor(Math.random() * people.length)].age); randomAges.forEach(randomAge => { it(`passes for random data #${i} and age ${randomAge}`, () => { expect(totalAgeYoungerThan(people, randomAge)).to.deep.eq(solution(people, randomAge)); }); }); }); }); }); })
- const chai = require('chai');
- const expect = chai.expect;
- chai.config.truncateThreshold = 0;
const totalAgeYoungerThan = totalAgeYoungerThan3; // define function to actually test hereconst totalAgeYoungerThanSolution = (people, age) => people.filter(person => person.age < age).reduce((sum, person) => sum + person.age, 0) ; // your function names are getting too long ..- const solution = (people, age) => people.filter(person => person.age < age).reduce((sum, person) => sum + person.age, 0);
- const reduce = target => (fn,z) => target.reduce(fn,z) ;
- const map = function map(fn) { return this.reduce( (z,x,i,xs) => [ ...z, fn(x,i,xs) ] , [] ); } ; // `this` requires `function` syntax
- const filter = function(pred) { return this.reduce( (z,x,i,xs) => pred(x,i,xs) ? [ ...z, x ] : z , [] ); } ; // idem
const target = [- function reducible(target) {
- return Object.freeze({ map, reduce: reduce(target.map(el => Object.freeze(el))), filter});
- }
- const people = reducible([
- {age: 10, hairColor: "brown"},
- {age: 30, hairColor: "red"},
- {age: 20, hairColor: "brown"},
- {age: 40, hairColor: "black"},
];- ]);
- const empty = reducible([]);
const people = Object.assign( {}, { map, reduce: reduce(target), filter} ); // should probably be factored out. just pass `target` as an argumentconst empty = Object.assign( {}, { map, reduce: reduce([]), filter } );- // const totalAgeYoungerThan = totalAgeYoungerThan0;
- // const totalAgeYoungerThan = totalAgeYoungerThan1;
- // const totalAgeYoungerThan = totalAgeYoungerThan2;
- // const totalAgeYoungerThan = totalAgeYoungerThan3;
- const totalAgeYoungerThan = totalAgeYoungerThan4;
- // const totalAgeYoungerThan = totalAgeYoungerThan5;
describe("challenge", () => {let originalMap, originalFilter, originalReduce;let mapCallCount = 0, filterCallCount = 0, reduceCallCount = 0;- describe("totalAgeYoungerThan", () => {
- describe("totalAgeYoungerThan",() => {
- it("returns the correct answer", () => {
- expect(totalAgeYoungerThan(people, 30)).to.deep.eq(30);
- })
- it("returns 0 when there are no people", () => {
- expect(totalAgeYoungerThan(empty, 30)).to.deep.eq(0);
- })
- it("returns 0 when there are no people who meet the criteria", () => {
- expect(totalAgeYoungerThan(people, -1)).to.deep.eq(0);
- })
- });
- describe("random tests", () => {
const dataSets = []; // generation of random values should be factored out, and should generate our custom object instead of an array- const dataSets = [];
- for(let i = 0; i < 0; i++) {
- const hairColors = ["brown", "black", "blue", "red", "yellow", "orange"];
- const people = [];
- const numberOfPeople = Math.random() * 20 + 5;
- for(let j = 0; j < numberOfPeople; j++) {
- people.push({age: j, hairColor: hairColors[Math.floor(Math.random() * hairColors.length)]});
- }
people.forEach(person => Object.freeze(person));Object.freeze(people);dataSets.push(people);- dataSets.push(reducible(people)); // generates custom object instead of array
- }
- describe("totalAgeYoungerThan", () => {
- dataSets.forEach((people, i) => {
const randomAges = [ // just move this inside a for 1 to 3 looppeople[Math.floor(Math.random() * people.length)].age,people[Math.floor(Math.random() * people.length)].age,people[Math.floor(Math.random() * people.length)].age,];- const randomAges = [];
- for(let j = 0; j < 3; j++) randomAges.push(people[Math.floor(Math.random() * people.length)].age);
- randomAges.forEach(randomAge => {
- it(`passes for random data #${i} and age ${randomAge}`, () => {
expect(totalAgeYoungerThan(people, randomAge)).to.deep.eq(totalAgeYoungerThanSolution(people, randomAge));- expect(totalAgeYoungerThan(people, randomAge)).to.deep.eq(solution(people, randomAge));
- });
- });
- });
- });
- });
(Moved here per the discussion in https://www.codewars.com/kata/5fbc297af8ed8d0008e971c9/discuss)
Assume you have an array of Person objects that looks like this:
[
{age: 10, hairColor: "brown"},
{age: 30, hairColor: "red"},
{age: 20, hairColor: "brown"},
{age: 40, hairColor: "black"},
];
Create the following functions:
-
totalAgeYoungerThan
- returns the total ages of the people younger than the given age -
hairColorsYoungerThan
- returns an array of the hair colors of the people younger than the given age
In each of these new functions, you may not use for
loops, and instead must solve them using any of .map
, .filter
and/or .reduce
.
// this code should fail the tests
// const totalAgeYoungerThan = (people, age) => {
// let totalAge = 0;
// for (let i = 0; i < people.length; i++) {
// if (people[i].age < age) totalAge += people[i].age;
// }
// return totalAge;
// }
// this code should fail the tests
// const totalAgeYoungerThan = (people, age) => {
// let totalAge = 0;
// for (let person of people) {
// if (person < age) totalAge += person.age;
// }
// return totalAge;
// }
// this code should fail the tests
// const totalAgeYoungerThan = (people, age) => {
// if (people.length === 0) return 0;
// const [first, ...rest] = people;
// return (first.age < age ? first.age : 0) + totalAgeYoungerThan(rest, age);
// }
// this code should theoretically pass the tests
// it includes the word `for` but not in a for loop
// const totalAgeYoungerThan = (people, age) => {
// return people
// .filter(person => person.age < age)
// .reduce((formula, person) => formula + person.age, 0)
// }
const totalAgeYoungerThan = (people, age) => {
return people
.filter(person => person.age < age)
.reduce((sum, person) => sum + person.age, 0)
}
const hairColorsYoungerThan = (people, age) => {
return people
.filter(person => person.age < age)
.map(person => person.hairColor)
}
const chai = require('chai');
const expect = chai.expect;
chai.config.truncateThreshold = 0;
const people = [
{age: 10, hairColor: "brown"},
{age: 30, hairColor: "red"},
{age: 20, hairColor: "brown"},
{age: 40, hairColor: "black"},
];
people.forEach(person => Object.freeze(person));
Object.freeze(people);
describe("challenge", () => {
let originalMap, originalFilter, originalReduce;
let mapCallCount = 0, filterCallCount = 0, reduceCallCount = 0;
beforeEach(function() {
originalMap = Array.prototype.map;
originalFilter = Array.prototype.filter;
originalReduce = Array.prototype.reduce;
Array.prototype.map = function() {
mapCallCount++;
return originalMap.apply(this, arguments);
}
Array.prototype.filter = function() {
filterCallCount++;
return originalFilter.apply(this, arguments);
}
Array.prototype.reduce = function() {
reduceCallCount++;
return originalReduce.apply(this, arguments);
}
});
afterEach(function() {
Array.prototype.map = originalMap;
Array.prototype.filter = originalFilter;
Array.prototype.reduce = originalReduce;
});
describe("totalAgeYoungerThan",() => {
it("returns the correct answer", () => {
expect(totalAgeYoungerThan(people, 30)).to.deep.eq(30);
})
it("returns 0 when there are no people", () => {
expect(totalAgeYoungerThan([], 30)).to.deep.eq(0);
})
it("returns 0 when there are no people who meet the criteria", () => {
expect(totalAgeYoungerThan(people, -1)).to.deep.eq(0);
})
it("does not use a for loop", () => {
// TODO: toString can be overwritten - find out how to read solution.txt?
expect(totalAgeYoungerThan.toString()).to.not.include("for");
})
it("uses reduce", () => {
totalAgeYoungerThan(people, 30);
expect(reduceCallCount).to.be.at.least(1);
})
});
describe("hairColorsYoungerThan",() => {
it("returns the correct answer", () => {
expect(hairColorsYoungerThan(people, 30)).to.deep.eq(["brown", "brown"]);
})
it("returns an empty array when there are no people", () => {
expect(hairColorsYoungerThan([], 30)).to.deep.eq([]);
})
it("returns an empty array when there are no people who meet the criteria", () => {
expect(hairColorsYoungerThan(people, -1)).to.deep.eq([]);
})
it("does not use a for loop", () => {
expect(hairColorsYoungerThan.toString()).to.not.include("for");
})
it("does use .map and .filter", () => {
totalAgeYoungerThan(people, 30);
expect(mapCallCount).to.be.at.least(1);
expect(filterCallCount).to.be.at.least(1);
})
});
describe("random tests", () => {
const dataSets = [];
for(let i = 0; i < 10; i++) {
const hairColors = ["brown", "black", "blue", "red", "yellow", "orange"];
const people = [];
const numberOfPeople = Math.random() * 20 + 5;
for(let j = 0; j < numberOfPeople; j++) {
people.push({age: j, hairColor: hairColors[Math.floor(Math.random() * hairColors.length)]});
}
people.forEach(person => Object.freeze(person));
Object.freeze(people);
dataSets.push(people);
}
describe("totalAgeYoungerThan", () => {
const totalAgeYoungerThanSolution = (people, age) => {
return people
.filter(person => person.age < age)
.reduce((sum, person) => sum + person.age, 0)
}
dataSets.forEach((people, i) => {
const randomAges = [
people[Math.floor(Math.random() * people.length)].age,
people[Math.floor(Math.random() * people.length)].age,
people[Math.floor(Math.random() * people.length)].age,
];
randomAges.forEach(randomAge => {
it(`passes for random data #${i} and age ${randomAge}`, () => {
expect(totalAgeYoungerThan(people, randomAge)).to.deep.eq(totalAgeYoungerThanSolution(people, randomAge));
});
});
});
});
describe("hairColorsYoungerThan", () => {
const hairColorsYoungerThanSolution = (people, age) => {
return people
.filter(person => person.age < age)
.map(person => person.hairColor)
}
dataSets.forEach((people, i) => {
const randomAges = [
people[Math.floor(Math.random() * people.length)].age,
people[Math.floor(Math.random() * people.length)].age,
people[Math.floor(Math.random() * people.length)].age,
];
randomAges.forEach(randomAge => {
it(`passes for random data #${i} and age ${randomAge}`, () => {
expect(hairColorsYoungerThan(people, randomAge)).to.deep.eq(hairColorsYoungerThanSolution(people, randomAge));
});
});
});
});
});
})