Related
I'm learning Javascript and I'm wondering what the most elegant way to convert this: [1,8]
into this:[1,2,3,4,5,6,7,8]?
Thanks a lot!
const argarray = [1, 8]
const countToN = (array) => {
// init results array
let res = []
// start at the first value array[0] go *up to* the second array[1]
for (let i = array[0]; i <= array[1]; i++) {
res.push(i)
}
// return the result
return res
}
console.log(countToN([1, 10]))
This would accommodate what you're trying to do, but it's fairly brittle. You'd have to check that it's an array and that it has only 2 values. If you had other requirements, I could amend this to account for it.
Here's a solution without loops. Note that this only works with positive numbers. It supports arrays of any length, but will always base the result off of the first and last values.
const case1 = [1, 8];
const case2 = [5, 20];
const startToEnd = (array) => {
const last = array[array.length - 1];
const newArray = [...Array(last + 1).keys()];
return newArray.slice(array[0], last + 1);
};
console.log(startToEnd(case1));
console.log(startToEnd(case2));
Here's a solution that works for negative values as well:
const case1 = [-5, 30];
const case2 = [-20, -10];
const case3 = [9, 14];
const startToEndSolid = (array) => {
const length = array[array.length - 1] - array[0] + 1;
if (length < 0) throw new Error('Last value must be greater than the first value.');
return Array.from(Array(length)).map((_, i) => array[0] + i);
};
console.log(startToEndSolid(case1));
console.log(startToEndSolid(case2));
console.log(startToEndSolid(case3));
A simple for loop will do it. Here's an example that has error checking and allows you to range both backwards and forwards (ie [1, 8], and also [1, -8]).
function range(arr) {
// Check if the argument (if there is one) is
// an array, and if it's an array it has a length of
// of two. If not return an error message.
if (!Array.isArray(arr) || arr.length !== 2) {
return 'Not possible';
}
// Deconstruct the first and last elements
// from the array
const [ first, last ] = arr;
// Create a new array to capture the range
const out = [];
// If the last integer is greater than the first
// integer walk the loop forwards
if (last > first) {
for (let i = first; i <= last; i++) {
out.push(i);
}
// Otherwise walk the loop backwards
} else {
for (let i = first; i >= last; i--) {
out.push(i);
}
}
// Finally return the array
return out;
}
console.log(range([1, 8]));
console.log(range('18'));
console.log(range());
console.log(range([1]));
console.log(range([-3, 6]));
console.log(range([9, 16, 23]));
console.log(range([4, -4]));
console.log(range([1, -8, 12]));
console.log(range(null));
console.log(range(undefined));
console.log(range([4, 4]));
Additional documentation
Destructuring assignment
Use Array#map as follows:
const input = [1,8],
output = [...Array(input[1] - input[0] + 1)]
.map((_,i) => input[0] + i);
console.log( output );
I am trying to create a little project where I can count the number of elements in an array. This part I have already done. My question is how can I fix a counting problem? I'm using a mapping method to get my element occurrence counter, but I want to put the data into an array. The only way I know how is to take the data from the mapping with .get. They way I'm doing it is by using this first part here:
let winners = [1, 2, 3, 2];
let nullArray = [];
let mode = new Map([...new Set(winners)].map(
k => [k, winners.filter(t => t === k).length]
));
for (let n in winners) {
nullArray.push(mode.get(winners[n]));
console.log(nullArray);
}
However, this will *n push matches. Like, if you have l=[1,2,2], because l[1]=2, and l[2]=2, they will be pushed into the nullArray as 2, however, it will also push l[2] and l[1] as the values are different. If you have three matches, it would duplicate 3 times, and so on. To counter this, I tried making a detector that would calculate when the same numbers in the nullArray are from the same copy but in a different order. To do this, I used the code I have below (combined with the original code)
let winners = [1, 2, 3, 2];
let nullArray = [];
let mode = new Map([...new Set(winners)].map(
k => [k, winners.filter(t => t === k).length]
));
for (let n in winners) {
nullArray.push(mode.get(winners[n]));
console.log(nullArray);
}
for (let num in nullArray) {
for (let c in nullArray) {
if (nullArray[num] === nullArray[c] && winners[num] === winners[c]) {
nullArray.splice(num, 1);
}
console.log(nullArray);
}
}
However, whenever I try this, the specific output on this array is [2,2]. How could I make a general solution that will eliminate all duplicate copies, only leaving a single copy of the number count (which in the case of [1,2,3,2], I would want nullArray=[1,2,1] as an output)
You can do something like this.
If you don't care about the order, you can just do the following.
const remove_dup_and_count = (winners = [1, 2, 3, 2]) =>{
let map = {}
//count elements
for (let n in winners) {
const curr_val = winners[n]
//duplicate, so we increment count
if(curr_val in map) map[curr_val] += 1
//first seen, so we start at 1
else map[curr_val] = 1
}
//lets grab the array of all keys in map
const keys_arr = Object.keys(map)
let count_arr = []
for(let i of keys_arr){
count_arr.push(map[i])
}
return count_arr
}
console.log(remove_dup_and_count())
If you care about the order, this is your best bet:
const remove_dup_and_count = (winners = [1, 2, 3, 2]) =>{
let map = new Map()
//count elements
for (let n in winners) {
const curr_val = winners[n]
//duplicate, so we increment count
if(map.get(curr_val)) map.set(curr_val, map.get(curr_val) + 1)
//first seen, so we start at 1
else map.set(curr_val,1)
}
let count_arr = []
//lets grab the array of all keys in map
for (const [key, value] of map) {
count_arr.push(value)
}
return count_arr
}
console.log(remove_dup_and_count())
I think you can use .reduce() method and then retrieve how many times the value is repeated in array using map.values(); something like the following snippet:
const winners = [1, 2, 3, 2];
const mapWinners = winners.reduce((winnersAccumulator, singleWinner) => winnersAccumulator.set(singleWinner, (winnersAccumulator.get(singleWinner) || 0) + 1), new Map())
console.log([...mapWinners.values()])
Problem:
Given an array of integers, return a new array such that each element at index i of the new array is the product of all the numbers in the original array except the one at i.
For example:
if our input was [1, 2, 3, 4, 5], the expected output would be [120, 60, 40, 30, 24].
If our input was [3, 2, 1], the expected output would be [2, 3, 6].
Solution 1 (With Nested loops): I'm able to solve this by nested loops like below:
const input = [1, 2, 3, 4, 5];
function output(items) {
const finalArray = [];
for (let i = 0; i < items.length; i++) {
let multipliedNum = 1;
items.forEach((item, indx) => {
if (i !== indx) {
multipliedNum = multipliedNum * item;
}
});
finalArray.push(multipliedNum)
}
return finalArray;
}
console.log(output(input))
I'm trying to find out another solution without nested loops inside output function? Any help or suggestion really appreciated.
If there are no zero values, you can loop through all the values once to get the product. Then just return the array where each the product is divided by each entry.
However, if there are zeros then there is a little more to be done to check how many there are. One zero is fine but more than 1 means that the value is zero for each entry.
const input = [1, 2, 3, 4, 5];
const input2 = [1, 2, 3, 4, 0];
const input3 = [1, 2, 3, 0, 0];
function output(items) {
let zeroCount = 0;
let totalProduct = 1;
for (let i = 0; i < items.length; i++) {
if (items[i] === 0) {
if (++zeroCount > 1) break;
continue;
}
totalProduct *= items[i];
}
if (zeroCount > 1) {
// more than 1 zero -> all values are 0
return new Array(items.length).fill(0);
} else if (zeroCount === 1) {
// only 1 zero -> only the value that is zero will be the totalProduct
return items.map(item => item === 0 ? totalProduct : 0);
}
// no zero in array -> divide the totalProduct by each item
return items.map(item => totalProduct / item);
}
console.log(output(input))
console.log(output(input2))
console.log(output(input3))
Based on what #Mike said in the comment here's the answer.
const input = [1, 2, 3, 4, 5];
const mulValues = input.reduce((acc, next) => acc * next);
const output = input.map(i => mulValues/i)
console.log(output)
you can do something like that (assuming array doesn't contain zero):
calculate product of all array elements
divide product by element at position [i] to get the desired output
const input = [1, 2, 3, 4, 5];
function output(items) {
const finalArray = [];
const multipliedNum=1;
for (let i = 0; i < items.length; i++) {
multipliedNum *= item[i];
}
for (let i = 0; i < items.length; i++) {
finalArray.push(multipliedNum/item[i]);
}
return finalArray;
}
console.log(output(input))
I know this has already been answered, but I think I have a better one.
If you take this issue by a different approach you will see that the product leaving the value at the index out, is also the product devided by value at the index.
If you know use the reduce function, you can simply calculate the product in one line using:
items.reduce((a, b) => a * b)
and then just divide by the value you want to ignore... like this:
items.reduce((a, b) => a * b) / items[index]
if you now want to compress this in one line instead of wrapping it into a for loop block you can simply copy the array and use the map function and the result could look like this:
result = [...items].map((v, i) => items.reduce((a, b) => a * b) / v)
I hope that this helps you to reduce your code
The aim of oddCount(n) is to output an array with odd positive numbers below n. Why .filter is not working inside oddCount(n)? .filter works only outside oddCount(n) in console.log(arr.filter(num => num%2)). There is something wrong, but I cannot spot it.
const arr = []
function oddCount(n){
for(let i = 0; i < n; i++) {
arr.push(i)
}
arr.filter(num => num%2)
}
oddCount(7)
console.log(arr)
// expected => arr = [1, 3, 5]
The filter is working just fine, the code just isn't doing anything with the new array it returns. If you want to over-write arr with that new array, assign it:
arr = arr.filter(num => num%2);
What is the best way to filter by a condition and take first n results in an array with circuit breaking?
Think a search filter on a predefined list of items:
[1, 2, 3, 4, 5, 6].filter(x => {
console.log(x);
return x < 4;
}).take(2) === [1, 2];
// 1
// 2
// true
the take function does not exist as the filter function only returns an array and i want the circuit breaking functionality for the stream. is there a composition using functional methods that would produce this?
i.e.
// would not process 3, 4, 5, or 6 as the circuit would break at 2.
take([1, 2, 3, 4, 5, 6], x => x < 4, 2) === [1, 2]
The function should also stop if no more elements remain. i.e.
take([1, 2, 3], x => x < 4, 4) === [1, 2, 3]
Thanks #GeraldoFurtado for the philisophical point. For loops are smart because they do their job, they're just old so get ageist remarks from all the hipster functional programmers like me.
let take = (arr, predicate, limit) => {
const results = [];
const arrLength = arr.length
for (let i = 0; i < arrLength && results.length < limit; i++) {
if (predicate(arr[i])) {
results.push(arr[i]);
}
}
return results;
}
Regardless, I would be keen on knowing anyone else's solutions to this problem, but this seems pretty performant to me... maybe a prototype shim solution?
You can accomplish this in a fairly elegant way using either generators or observables.
Generators
I have actually developed a little npm package specifically for this purpose.
Using that package, and Ramda, you can get your desired result like this:
Try it in RunKit
const { genTake, genFilter, genFrom, genToArray } = require("func-generators");
const { compose } = require('ramda');
const probe = x => (console.log(x),x);
const firstTwoUnderFour = compose(
genToArray,
genTake(2),
genFilter(x => probe(x) < 4), // genFilter(x => x < 4),
genFrom,
);
console.log(firstTwoUnderFour([5,6,7,1,6,8,2,9,10,5])); // [1, 2]
I have added a probe to the filter predicate to show which elements it's processing. You can see from the console output that it stops as soon as it reaches 2.
Basically, what it's doing here is:
Make a generator function from the original array (genFrom)
Transform that generator function to one that only produces the values that match the predicate (genFilter(x => x < 4))
Transform that generator function to one that only produces the first two values from the one in step 2 (genTake(2))
Converge the generator function from step #3 into an array of values (genToArray)
The more generalized take from the answer you posted could then be written like this:
const take = (arr, predicate, limit) => compose(
genToArray,
genTake(limit),
genFilter(predicate),
genFrom,
)(arr);
This even works on an infinite sequence of values:
Try it in RunKit
const { genTake, genFilter, genInfinite, genToArray } = require("func-generators");
const { compose } = require('ramda');
const firstFiveMultiplesOf125 = compose(
genToArray,
genTake(5),
genFilter(x => x % 125 === 0),
);
console.log(firstFiveMultiplesOf125(genInfinite()));
One last thing to note: I'm using Ramda's compose for readability here, but you could just as well nest these function calls instead, or call them one-by-one:
const result = genToArray(gnTake(5, filter(x => x < 4, genFrom([1,2,3,4,5,6]))));
// or...
const gen = genFrom([1, 2, 3, 4, 5, 6]);
const first2 = genTake(2, genFilter(x => x < 4, gen));
const result = genToArray(first2);
Observables
One slightly more heavyweight approach is to use Observables, such as the RxJS library:
Try it in RunKit
const { from } = require("rxjs")
const { take, filter, toArray } = require('rxjs/operators');
const probe = x => (console.log(x),x);
from([5,6,7,1,6,8,2,9,10,5]).pipe(
filter(x => probe(x) < 4),
take(2),
toArray(),
).subscribe(xs => console.log(xs)); // [1, 2]
You can see here too that it stops checking elements from the array as soon as it reaches 2.
You could take a for ... of loop with a lazy evaluation and exit if the result array has a length of two.
var array = [1, 2, 3, 4, 5, 6],
result = [];
for (let item of array) {
if (item < 4) if (result.push(item) === 2) break;
}
console.log(result);
Use lodash!
take(filter([1, 2, 3, 4, 5, 6], x => x < 4), 2)
const take = (array, predicate, length) => {
let result= []
let i = 0
while (i <= array.length && result.length < length) {
if (predicate(array[i])) {
result.push(array[i])
}
i++
}
return result;
}
const test1 = take([1, 2, 3, 4, 5, 6], x => x < 4, 4)
console.log(test1)
const test2 = take([1, 2, 3, 4, 5, 6, 7, 8, 9], x => x % 2 === 0, 5)
console.log(test2)
If good old for loop does not bring aesthetic satisfaction, you may use (in somewhat unnatural way) one of high-order functions with embedded circuit breaker if you're addict to them as much as I am:
const arr = [2,6,9,8,4,3,7,5];
const res = [];
//first 3 items less than 8
arr.find(n => n<8 ? (res.push(n), res.length==3) : false, res);
console.log(res);