I'm returning the 2nd, 3rd and 5th numbers in the string '123456' by using
function returnSome(numbers) {
return numbers[1] + numbers[2] + numbers[4]
}
returnSome('123456'); //2, 3, 5
Simple enough right? What I'm wondering is whether there is a shorter way to write that out? something like numbers[1][2][4]?
I can't seem to find an answer online! (It may well be that I just don't know the correct terminology!)
Not really shorter, but you could use an array of indices and map that to the strings characters, then join them to a new string:
[1, 2, 4].map(i => numbers[i]).join("")
something like numbers[1][2][4]?
We can get quite close with currying:
const wrap = (str, res = "") => index => index + 1 ? wrap(str, res + str[index]) : res;
wrap(numbers)(1)(2)(4)()
You can turn the indices you want into a string that you iterate over and pull out from the array you pass in.
let returnSome = (numbers, sections) => sections.split("").map(i => +numbers[i]);
let returnSome = (numbers, sections) => sections.split("").map(i => +numbers[i]);
console.log( returnSome('123456', '124') ); //2, 3, 5
Related
I'm wondering how to convert an array of strings
lines = ['101','010','110'];
to an array of arrays like this:
x = [
[1,0,1],
[0,1,0],
[1,1,0]'
]
I already tried
x = (lines.forEach(e => (e.split(''))))
and realized String.split doesnt mutate the current string. So my next step was to create a new array with these values.
x = new Array(lines.forEach(e => (e.split(''))))
My thoughts behind this line:
The code should take an element (e) of the lines array and apply the split funtion to it. (which is does when i console.log() it.) BUT it doesnt apply it to the new array.
Maybe the problem is, that it doesn't loop through x but maybe i overlook another fact.
You can use .map(Number) on the split() result to convert them to a Number as expected
const lines = ['101','010','110'];
const res = lines.map(l => l.split('').map(Number));
console.log(res);
[
[
1,
0,
1
],
[
0,
1,
0
],
[
1,
1,
0
]
]
Regarding your forEach solution, since forEach does not return anything, x stays undefined
You could define an empty array, and push your split() into that empty array, but using map() is a more readable/clean solution.
For more information about map() vs forEach(), please take a look at this stackoverflow answer.
As per #OstoneO's answer, Array#map() is the most appropriate method to use, hence it would be my first choice.
Array#forEach()'s return value is undefined because it's not designed to return a value; it is a loop and should be used as such:
const lines = ['101','010','110'];
const x = [];
lines.forEach( line => x.push( line.split('').map(n => +n) ) );
console.log( x );
Using Array.prototype.reduce method,
['101','010','110'].reduce((acc,val,index)=>{
acc[index] = [...val.split("").map((item)=>parseInt(item))];
return acc;
}
,[]);
Background information: I have an array
this.someArray = ["Word", "123", "456"]
Where this.someArray is dynamically written (the array elements are not hardcoded)
I need to convert all items that are numbers into numbers (yes I realise that this might not make sense, essentially this is the result I want - where the numbers don't have quotes but leave the words as they are):
["Word", 123, 456]
So the steps I've thought in terms of how to achieve this:
Find out whether each element in the array is a word or number
To achieve this I have:
isNumber(number) {
return !isNaN(parseFloat(number)) && !isNaN(number-0)
}
Use a for each loop to test whether each element is a word or number
this.someArray.forEach(element => {
this.isNumber(element)
});
Write an if statement (if the element in this.someArray is a number then remove the quotes from that element)
However I'm unsure of whether step 2 is actually the correct thing to do and I'm unsure of how to write step 3
Is there a way to accomplish this?
Further info:
This is what the dynamically generated array looks like:
This is the code:
this.someArray = this.biggerArray.map((n) => {
const data = [];
for (var key of Object.keys(n)) {
data.push(n[key].data);
}
return data;
});
I think a plain .map would be easier - check if the string is composed of all digits with a regular expression, and if so, call Number on it:
const arr = ["Word", "123", "456"];
const newArr = arr.map(
str => /^\d+$/.test(str) ? Number(str) : str
);
console.log(newArr);
^\d+$ means:
^ - start of string
\d+ - one or more digits
$ - end of string
If the numbers might contain decimals, then add an optional group for the decimal portion:
const arr = ["Word", "123", "456", '12.45'];
const newArr = arr.map(
str => /^\d+(?:\.\d+)?$/.test(str) ? Number(str) : str
);
console.log(newArr);
For the array of ['Process', '1287'], it still works as expected:
const arr = ['Process', '1287'];
const newArr = arr.map(
str => /^\d+(?:\.\d+)?$/.test(str) ? Number(str) : str
);
console.log(newArr);
This approach also works for decimals within quotes.
for (let i in someArray) {
if (parseFloat(someArray[i])) {
someArray[i] = parseFloat(someArray[i]);
}
}
This is a shorter way of doing it.
for (let i in someArray) {
parseFloat(someArray[i]) && (someArray[i] = parseFloat(someArray[i]));
}
I am trying to make a string that lists out the dates of a month. For example, I need to make a string that lists out:
dayInDateOfBirthInput: "Day\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31",
I've tried doing this
[Array(31).keys()].map(x => x+1).join("\n")
Thanks for looking!
I've tried doing this [Array(31).keys()].map(x => x+1).join("\n")
You are very close. .keys() returns an Array Iterator object. You need to spread it to create an array.
console.log(
[...Array(31).keys()].map(x => x+1).join("\n")
)
Your code from the comments can be tweaked:
Array(31).fill(0).map((_,x) => x+1).join("\n");
Something like
const str = 'Day' + Array(31).fill(0).map((x, i) => `\n${i+1}`).join('');
console.log(str);
As others have pointed out, your example is missing a spread operator.
Might I suggest using Array.from(...) (of which the second argument is a map function):
let result = Array.from({length:31}, (_,i) => i+1).join("\n");
console.log(result);
let resultWithDay = `Day\n${result}`;
console.log(resultWithDay);
To reverse it:
let result = Array.from({length:31}, (_,i) => i+1).reverse().join("\n");
console.log(result);
let resultWithDay = `${result}\nDay`;
console.log(resultWithDay);
I want to sort an array of phone numbers and have the length of the array outputted based on areacode. For example:
var nums = [
8881756223,
8881742341,
9187221757,
...,
]
there are a lot more entries than that (roughly 1300) and its already in numerical order. However, what I want it to do is:
1. look at the first 3 numbers of the first entry
2. look at the next entries first 3 numbers
3. if they are different, then splice the array, console.log new array.length
and console.log that area code
so for example, the first two numbers in the array i provided will be spliced into their new array, and the console output will be:
areacode: 888, length: 1
areacode: 918, length: 0
I know the regex to search for the first the numbers, but I don't exactly know how to splice them into their own arrays...Like i know, use splice, but comparing the two with logic statements, I've never had to do something like that before while using a regular expression.
what I have so far is this:
const patt = new RegExp('^\d{3}')
var newArr = nums.filter(x => patt)
for (var i = 0; i < newArr.length; i++)
console.log(newArr[i])
but this is spitting out the full number, not the area code its self. Of course ill be adding the logic to sort after i get it to just spit out area codes.
I suggest using
nums.map(x => ("" + x).replace(/^(\d{3})[^]*/, '$1'))
Here,
"" + x will coerce the number to string
.replace(/^(\d{3})[^]*/, '$1') will remove all chars keeping the first 3 digits (or the whole string upon no match).
JS Demo:
var nums = [
8881756223,
8881742341,
9187221757,
1
];
var res = nums.map(x => ("" + x).replace(/^(\d{3})[^]*/, '$1'));
console.log(res);
you can try this
var nums = [
8881756223,
8881742341,
9187221757
]
var prefixes = nums.map(x=>(x+"").substr(0,3));
var votes = prefixes.reduce(
(votes, curr) => {
if(votes[curr]) votes[curr]++;
else {votes[curr] =1;}
return votes;
}, {});
var ans = Object.keys(votes).map(x => ({areacode:x, length:votes[x]}));
console.log(ans);
ans will hold the value you require
vote counting technique i used is explained here https://igghub.github.io/2017/01/15/useful-js-reduce-trick/
I was wondering what would be the best way to split an array into two different arrays using JavaScript, but to keep it in the realms of functional programming.
Let's say that the two arrays should be created depending on some logic. For instance splitting one array should only contain strings with less than four characters and the other the rest.
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
I have thought about different methods:
Filter:
const lessThanFour = arr.filter((animal) => {
return animal.length < 4;
});
const fourAndMore = arr.filter((animal) => {
return animal.length >= 4;
});
The problem with this for me is that you have to go through your data twice, but it is very readable. Would there be a massive impact doing this twice if you have a rather large array?
Reduce:
const threeFourArr = arr.reduce((animArr, animal) => {
if (animal.length < 4) {
return [[...animArr[0], animal], animArr[1]];
} else {
return [animArr[0], [...animArr[1], animal]];
}
}, [[], []]);
Where the array's 0 index contains the array of less than four and the 1 index contains the array of more than three.
I don't like this too much, because it seems that the data structure is going to give a bit of problems, seeing that it is an array of arrays. I've thought about building an object with the reduce, but I can't imagine that it would be better than the array within an array solution.
I've managed to look at similar questions online as well as Stack Overflow, but many of these break the idea of immutability by using push() or they have very unreadable implementations, which in my opinion breaks the expressiveness of functional programming.
Are there any other ways of doing this? (functional of course)
collateBy
I just shared a similar answer here
I like this solution better because it abstracts away the collation but allows you to control how items are collated using a higher-order function.
Notice how we don't say anything about animal.length or < 4 or animals[0].push inside collateBy. This procedure has no knowledge of the kind of data you might be collating.
// generic collation procedure
const collateBy = f => g => xs => {
return xs.reduce((m,x) => {
let v = f(x)
return m.set(v, g(m.get(v), x))
}, new Map())
}
// custom collator
const collateByStrLen4 =
// collate by length > 4 using array concatenation for like elements
// note i'm using `[]` as the "seed" value for the empty collation
collateBy (x=> x.length > 4) ((a=[],b)=> [...a,b])
// sample data
const arr = ['horse','elephant','dog','crocodile','cat']
// get collation
let collation = collateByStrLen4 (arr)
// output specific collation keys
console.log('greater than 4', collation.get(true))
console.log('not greater than 4', collation.get(false))
// output entire collation
console.log('all entries', Array.from(collation.entries()))
Check out that other answer I posted to see other usage varieties. It's a pretty handy procedure.
bifilter
This is another solution that captures both out outputs of a filter function, instead of throwing away filtered values like Array.prototype.filter does.
This is basically what your reduce implementation does but it is abstracted into a generic, parameterized procedure. It does not use Array.prototype.push but in the body of a closure, localized mutation is generally accepted as OK.
const bifilter = (f,xs) => {
return xs.reduce(([T,F], x, i, arr)=> {
if (f(x, i, arr) === false)
return [T, [...F,x]]
else
return [[...T,x] ,F]
}, [[],[]])
}
const arr = ['horse','elephant','dog','crocodile','cat']
let [truthy,falsy] = bifilter(x=> x.length > 4, arr)
console.log('greater than 4', truthy)
console.log('not greater than 4', falsy)
Though it might be a little more straightforward, it's not nearly as powerful as collateBy. Either way, pick whichever one you like, adapt it to meet your needs if necessary, and have fun !
If this is your own app, go nuts and add it to Array.prototype
// attach to Array.prototype if this is your own app
// do NOT do this if this is part of a lib that others will inherit
Array.prototype.bifilter = function(f) {
return bifilter(f,this)
}
The function you are trying to build is usually known as partition and can be found under that name in many libraries, such as underscore.js. (As far as I know its not a builtin method)
var threeFourArr = _.partition(animals, function(x){ return x.length < 4 });
I don't like this too much, because it seems that the data structure is going to give a bit of problems, seeing that it is an array of arrays
Well, that is the only way to have a function in Javascript that returns two different values. It looks a bit better if you can use destructuring assignment (an ES6 feature):
var [smalls, bigs] = _.partition(animals, function(x){ return x.length < 4 });
Look at it as returning a pair of arrays instead of returning an array of arrays. "Array of arrays" suggests that you may have a variable number of arrays.
I've managed to look at similar questions online as well as Stack Overflow, but many of these break the idea of immutability by using push() or they have very unreadable implementations, which in my opinion breaks the expressiveness of functional programming.
Mutability is not a problem if you localize it inside a single function. From the outside its just as immutable as before and sometimes using some mutability will be more idiomatic than trying to do everything in a purely functional manner. If I had to code a partition function from scratch I would write something along these lines:
function partition(xs, pred){
var trues = [];
var falses = [];
xs.forEach(function(x){
if(pred(x)){
trues.push(x);
}else{
falses.push(x);
}
});
return [trues, falses];
}
A shorter .reduce() version would be:
const split = arr.reduce((animArr, animal) => {
animArr[animal.length < 4 ? 0 : 1].push(animal);
return animArr
}, [ [], [] ]);
Which might be combined with destructuring:
const [ lessThanFour, fourAndMore ] = arr.reduce(...)
If you are not opposed to using underscore there is a neat little function called groupBy that does exactly what you are looking for:
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
var results = _.groupBy(arr, function(cur) {
return cur.length > 4;
});
const greaterThanFour = results.true;
const lessThanFour = results.false;
console.log(greaterThanFour); // ["horse", "elephant", "crocodile"]
console.log(lessThanFour); // ["dog", "cat"]
Kudos for the beautiful response of the user Thank you, here an alternative using a recursion,
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
const splitBy = predicate => {
return x = (input, a, b) => {
if (input.length > 0) {
const value = input[0];
const [z, y] = predicate(value) ? [[...a, value], b] : [a, [...b, value]];
return x(input.slice(1), z, y);
} else {
return [a, b];
}
}
}
const splitAt4 = splitBy(x => x.length < 4);
const [lessThan4, fourAndMore ] = splitAt4(arr, [], []);
console.log(lessThan4, fourAndMore);
I don't think there could be another solution than returning an array of arrays or an object containing arrays. How else is a javascript function return multiple arrays after splitting them?
Write a function containing your push logic for readability.
var myArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var x = split(myArr, v => (v <= 5));
console.log(x);
function split(array, tester) {
const result = [
[],
[]
];
array.forEach((v, i, a) => {
if (tester(v, i, a)) result[0].push(v);
else result[1].push(v);
});
return result;
}