I'm kind of new to functional programming and I try to do the following: Let's say I have an array of values: ['a','b-','c'] and I want that every item which ends with a '-' is merged with the following array entry: ['a','b-c'].
Of course I can do this by making a for-loop:
var test = ['a', 'b-', 'c'], result = [];
for (var i=0;i<test.length;i++) {
var curr = test[i];
if (curr.endsWith('-')) {
curr += test[i+1];
i++;
}
result.push(curr);
}
But how can one do this without the for loop?
To be honest, the way you programmed is probably the most efficient way to do this.
However, here's another option:
var test = ['a', 'b-', 'c'],
result = test.join().replace(/-,/g, '').split(',');
console.log(result);
This joins all elements into a string: 'a,b-,c', removes all occurrences of '-,' > 'a,bc', then splits the string back up into an array with the desired result, as you can see in the output.
This can be fool-proofed a bit, by changing the separator used in the join / split:
var test = ['a', 'b-', 'c'],
separator = '||',
result = test.join(separator)
.replace(new RegExp('-' + separator, 'g'), '')
.split(separator);
One possible approach (with .reduce):
var arr = ['a', 'b-', 'c'];
var trans = arr.reduce(function(acc, cur) {
if (acc.length && acc[acc.length - 1].slice(-1) === '-') {
acc[acc.length - 1] += cur;
}
else {
acc.push(cur);
}
return acc;
}, []);
This can also be achieved using Array.prototype.map:
var test = ['a', 'b-', 'c'];
var result = test.slice().map(function (x, i, a) {
if (x.endsWith("-") && a[i+1]) {
var r = x + a[i+1]; // Join this and the next element in the array
a.splice(i, 1); // Remove the next element from the array
return r;
}
return x;
}).filter(function (x) {
return typeof x !== 'undefined';
}); // Since the array comes back with a different length and some undefined elements, remove those. Thanks #Cerbrus for pointing this out
console.log(test, result, result.length); // ["a", "b-", "c"] ["a", "b-c"] 2
This way will work for multiple dashed elements in a row, and if the last element has a dash, uses Array.forEach
var test = ['a', 'b-', 'c-'], result = [], next = "";
test.forEach(function(curr) {
if (curr.endsWith('-')) {
next += curr;
if (curr == test[test.length-1]) {
result.push(next);
}
}else {
result.push(next + curr);
next = "";
}
});
document.write(result);
Another map + filter one. Most likely slower, as filter add's another iteration through the array, but works as the original does (which is probably not what the OP wants when there are multiple -'s in a row).
var test = ['a', 'b-', 'c-', 'd', 'e'], result = [];
result = test
.map((curr, i, array) => (curr.endsWith('-') && array[i + 1] !== undefined) ? curr + array[i+1] : curr)
.filter((curr, i, arr) => (i>0 && arr[i-1].length > 1 && curr.length === 1) ? false : true)
document.write(result);
Didn't read all answers, so sry if I repeat sth. that has already been said.
Functional programming doesn't mean that there is always a predefined function that does exactly what you intend to; or a combination of some.
Sometimes it is better to write a simple short utility-function than abusing the ones that are already there.
How about some "problems" like multiple dashed-values next to each other, or at the end of the list? How do you want to handle these cases?
This would be my implementation:
function combineDashedStrings(arr){
var result = [], pending = ""
for (var i=0; i<arr.length; i++) {
var curr = pending + arr[i];
pending = curr.endsWith("-") && curr || ""; //multiple concats
//pending = !pending && curr.endsWith("-") && curr || ""; //single concat
pending || result.push(curr);
}
//pending && result.push(curr); //add last item if it contains a dash
return result
}
combineDashedStrings(['a', 'b-', 'c-', 'd', 'e-']);
feel free to switch the commented lines/options
Related
Have to create a function that return the sum of the element in the array but if the array is
["a","b","c"] // output : abc
So far I have
function calculateSumRecursion(array) {
//your code
if (array.length === 0 ) {
return 0
}
return array[0] + calculateSumRecursion(array.slice(1))
}
I found out how to calculate the sum of all numbers using recursion but when it's an array of string like
array = ["a","b","c"]
it returns me
// abc0
because of the if statement.. is there any way to say
if (array.length === 0) return nothing instead of a 0 (that work only when it's an array of number?)
You just need to return the only value in the array when the length is 1, rather than waiting until you get a length of 0. That way you are always summing compatible types (numbers or strings). Note that you still need a test for a 0 array length in case the function gets called with an empty array. In this case you need to choose what to return; as requested, it is 0.
function calculateSumRecursion(array) {
if (array.length === 0) {
return 0;
}
if (array.length === 1) {
return array[0];
}
return array[0] + calculateSumRecursion(array.slice(1))
}
console.log(calculateSumRecursion([1, 2, 3, 4, 5]));
console.log(calculateSumRecursion(['a', 'b', 'c']));
console.log(calculateSumRecursion([]));
let arr = [1,2,3,4,5] // output : abc
let sum = calculateSumRecursion(arr);
function calculateSumRecursion (arr) {
return arr.length ? arr.pop() + calculateSumRecursion(arr) : 0;
}
Slice version
let arr = [1,2,3,4,5] // output : abc
let sum = calculateSumRecursion(arr);
function calculateSumRecursion (arr) {
return arr.length ? arr[0] + calculateSumRecursion(arr.slice(1)) : 0;
}
Change return 0 to return "" which will add an empty string to the sum.
You have returned 0 when the array is empty.
Now, you are doing string operations so it is needed to return empty value (not zero) so it will be affordable to return "".
function calculateSumRecursion(array) {
return array.length === 0 ? "" : array[0] + calculateSumRecursion(array.slice(1));
}
There's a way easier way to do this:
function calculateSumRecursion(array) {
var out = array[0];
for (let i = 1; i < array.length; i++) {
out = out + array[i];
}
return out;
}
Return empty string on recursion base case. Just replace your return 0 to return ''.
const array = ['a', 'b', 'c'];
function calculateSumRecursion(array) {
if (array.length === 0) {
return '';
}
return array[0] + calculateSumRecursion(array.slice(1));
}
console.log(calculateSumRecursion(array));
If you are want to work with number also then check array length for zero as well as one.
const array = ['a', 'b', 'c', 'e'];
const array2 = [];
const array3 = [1, 2, 3];
function calculateSumRecursion(array) {
const rec =
array.length === 1
? array[0]
: array.length >= 1 && array[0] + calculateSumRecursion(array.slice(1));
return array.length === 0 ? 0 : rec;
}
console.log(calculateSumRecursion(array));
console.log(calculateSumRecursion(array2));
console.log(calculateSumRecursion(array3));
While I'm sure I'm waay over-complicating things, I'm curious to know how I would "collapse" an array by combining all adjacent strings, but leaving objects as objects so that:
array = ["I","want","to",[Obj],"come","together"]
outputs
["I want to", [Obj], "come together"];
I feel like array.reduce() might be the ticket here, but I'm still wrapping my head around that function.
Reduce the array. If the current item and the last item in the accumulator (r) are strings, concatenate them. If not, push the current item to the accumulator:
const array = ["I","want","to",[{}],"come","together"]
const isString = s => typeof s === 'string'
const result = array.reduce((r, o) => {
if(isString(o) && isString(r[r.length - 1])) {
r[r.length - 1] = `${r[r.length - 1]} ${o}`
} else {
r.push(o)
}
return r
}, [])
console.log(result)
I would do this:
const array = ["I","want","to",{},"come","together"];
let outputArr = [];
let strArr = [];
array.forEach(elem => {
if (typeof elem === 'string') {
return strArr.push(elem);
}
outputArr.push(strArr.join(' '));
strArr = [];
outputArr.push(elem);
});
outputArr.push(strArr.join(' '));
console.log(outputArr);
In case you wanna go with the plain old for-loop :)
var result = [], array = ["I","want","to",{a: 1, b:2},"come","together"];
var i=0;var str = "";
for(; i< array.length; i++){
if(typeof array[i] === 'object' ){
result.push(str);
result.push(array[i]);
str="";
}else{
str = str+" "+ array[i];
}
}
if(i==array.length){
result.push(str);
}
console.log(result);
I got this task and I can't get how to replace the matches with the string "BUSTED", can you help me please. I'm new to JS. Most probably there is a way more elegant way to do this. Any help appreciated.
You're given two arrays: one that holds every member of fCommunity and another one
that holds every possible suspect.
Replace every fCommunity member from the Suspect list with the word "BUSTED"
var fCommunityMembers = ['A','B','C'];
var SuspectList = ['F','X','B','Z','Y','C','ZS','D','K','M','N'];
I managed to retrieve the matching members but how do I replace them in the suspect list?:
Array.prototype.diff = function(SuspectList) {
var ret = [];
this.sort();
SuspectList.sort();
for(var i = 0; i < this.length; i += 1) {
if(SuspectList.indexOf( this[i] ) > -1){
ret.push( this[i] );
}
}
return ret;
};
var ListOfMatches = Array.from(fCommunityMembers.diff(SuspectList));
console.log( ListOfMatches );
Use Array.prototype.map() to iterate fCommunityMembers. If an member is found in the SuspectList return BUSTED, if not return the member:
var fCommunityMembers = ['A','B','C'];
var SuspectList = ['F','X','B','Z','Y','C','ZS','D','K','M','N'];
var result = fCommunityMembers.map(function(member) {
return SuspectList.indexOf(member) !== -1 ? 'BUSTED' : member;
});
console.log(result);
What you need is a function that can do the intersection between two arrays
filter can help in your case
var fCommunityMembers = [
'A',
'B',
'C',
'D',
'F'
];
var SuspectList = [
'F',
'X',
'B',
'Z',
'Y',
'C',
'ZS',
'D',
'L'
];
var suspects= fCommunityMembers.filter(function(el){
return SuspectList.indexOf(el)>-1;
});
Edit #1
To replace the values by busted, do the following:
var suspectsFnc = function (fc, sus) {
var busted = fCommunityMembers.filter(function (el) {
return SuspectList.indexOf(el) > -1;
});
for (var i = 0; i < fc.length; i++) {
if (busted.indexOf(fc[i]) > -1) {
fc[i] = "Busted";
}
}
return fc;
}
var suspects = suspectsFnc(fCommunityMembers, SuspectList);
Judt use filter like this :
var ListOfMatches = SuspectList.filter(x => fCommunityMembers.indexOf(x) !== -1);
and a forEach loop to insert BUSTED instead of previous matched elements
fCommunityMembers.forEach((x,i,arr) => {
if (SuspectList.indexOf(x) !== -1)
arr[i] = "BUSTED";
});
How can I check the array has empty element or not?
Imagine this array,
var arr = [ 'a', 'b', , 'd'];
the arr[2] is undefined. I want to check this. If the element has empty element, return 'true' or return false. Maybe like this,
function hasEmptyElement(array){
for (var i=0; i<array.length; i++){
if (typeof arr[i] == 'undefined'){
return true;
// and then ?
// should I use double for loop or helper variable?
}
}
}
I confuse how can I do this. Please help me the clevers.
As of ES2016, you should use Array.prototype.includes:
const array = ["a", "b", , "d"];
array.includes(undefined); // true
(You don't need to write undefined, but this makes it more clear what's happening.)
Note that this method treats slots valued undefined and empty slots the same, although they're not. If you need to differentiate these two cases as well, starting from ES2017, you can use Object.values making the following expression true if there are empty slots in the array:
Object.values(array).length !== array.length; // true
First, note the difference between empty slots and slots with undefined value:
var arr = [/*empty slot*/, undefined];
Object.keys(arr); // ["1"] but not "0"
"0" in arr; // false
"1" in arr; // true
ES5 array methods skip empty slots. ES6 [].includes does not.
That means you can use
arr.includes(undefined); // has empty slot OR contains undefined value
arr.indexOf(undefined) > -1; // contains undefined value
If you want to test only if there are empty slots, you can iterate manually with a for loop and check whether all indices between 0 and the length of the array are present, e.g. with in operator.
(function() {
for(var i=0; i<arr.length; ++i) if(!(i in arr)) return true;
return false;
})(); // has empty slot
Or you can also use ES5 array methods and check if they skipped an index.
var n = 0;
arr.some((_,i) => i !== n++); // has empty slot
You can do something like that:
function hasEmptyElement(array){
for (var i=0; i<array.length; i++){
if (!(i in array)) {
return true;
}
}
return false;
}
The main point of this implementation is that it makes difference between [1,,3] and [1, undefined, 3]
try
var hasAnyEmptyElement = arr.filter(function(val){ return (typeof val) != "undefined" }).length != arr.length;
DEMO
var arr = [1,2];
arr[4] = 2;
var hasAnyEmptyElement = arr.filter(function(val){ return (typeof val) != "undefined" }).length != arr.length;
alert(hasAnyEmptyElement);
You could also use Array.prototype.findIndex()
var arr = ['a', 'b', , 'd'];
document.write(arr.findIndex(e => e === undefined) > -1);
var temp = arr.filter(item => item);
This will give you a new array with below elements:
["a", "b", "d"]
The above solution will help to remove empty as well as null values.
Simple implementation using set
var arr = [ 'a', 'b', , 'd'];
// Using Set from es6
var arraySet = new Set(arr)
arraySet.has(undefined) // returns true
Thanks!
For ES5- you can do
var arr = [ 'a', 'b', , 'd'];
arr.filter(function() { return true }).length === arr.length
That is going to return false if there is a undefined
ES2015 check includes
You can try like this:
var arr = [ 'a', 'b',, 'd'];
function myfunc(arr) {
for(var i=0; i<arr.length; i++) {
if (!(i in arr)) return false;
}
return true;
}
alert( myfunc(arr));
`Check null/undefined values in Array`
function checkNullValue(a) {
if (typeof (a) == 'undefined' || a === null) {
return false;
} else {
return true;
}
}
var arrayMonthVal = ['1','2',,'6','null', '8'];
var pass = arrayMonthVal.some(checkNullValue);
I recently needed to know if a given array element was empty and this page helped me derive the following solution:
/** Determine if an array element is empty.
* #param {*[]} arr
* #param {number} [i] If not provided, check entire arr for empty.
* #return {boolean}
*/
const hasEmpty = (arr, i) => 0<=i
? !arr.some((_, j) => j==i)
: Object.values(arr).length!==arr.length;
Making it simple you can use the below comparison for achieving the same.
function hasEmptyElement(array){
for(var i=0;i<array.length;i++){
if(my_arr[i] === "")
return false;
}
return true;
}
hello try this....
if (typeof arr[i] == 'NULL'){
return true;
}
hope this may work
I am trying to check if an element exists in any one of three arrays. I don't know how to return the name of the array where the element was found. Can anyone direct me into the right direction please.
I have coded a function which takes the element in search as its argument and then returns the array name:
var arr1 = ['a','b','c','d'];
var arr2 = ['e','f','g','h'];
var arr3 = ['i','j','k','l'];
function chkElem(elem)
{
var id = elem;
var isFound = null;
if(arr1.indexOf(id) || (arr2.indexOf(id) || (arr3.indexOf(id))))
{
isFound = ????
}
return isFound;
}
I am uncertain how to assign the parent array name to 'isFound' variable.
Thanks.
You should never use "variable names" in your function logic. Instead, make the arrays properties of an object and return the property name:
var arrays = {
"arr1": ['a','b','c','d'],
"arr2": ['e','f','g','h'],
"arr3": ['i','j','k','l']
};
for (var name in arrays)
if (arrays[name].indexOf(id) > -1)
return name;
return null;
Or, even better, use an array of arrays to search in and return the index:
var arrays = [
['a','b','c','d'],
['e','f','g','h'],
['i','j','k','l']
];
for (var i=0; i<arrays.length; i++)
if (arrays[i].indexOf(id) > -1)
return i;
return -1;
Test one-by-one:
if (arr1.indexOf(id) > -1) {
isFound = arr1;
} else if (arr2.indexOf(id) > -1) {
isFound = arr2;
} else if (arr3.indexOf(id) > -1) {
isFound = arr3;
}
Alternatively, create a multi-dimensional array:
var arr = [
['a','b','c','d'],
['e','f','g','h'],
['i','j','k','l']
];
var isFound = null;
for (var i = 0; i < arr.length; i++) {
if (arr[i].indexOf(elem) > -1) {
isFound = arr[i];
break;
}
}
Firstly, be careful of the indexOf() trap - if it fails to find the requested string, it will return -1 - which is a truthy - so you need to check explicitly like so:
if (arr1.indexOf(id) != -1)
not
if (arr1.indexOf(id))
The truthy/falsy concept also means that, if your string is the first element in the array, and so indexOf() returns false, that is a falsy, and so your condition will actually fail even though a match was made!
Secondly, you cannot return the name of the array - or, to be more precise, the name of the variable that references it in the JS memory. You can either:
1) return the array itself
if (arr1.indexOf(id) != -1) return arr1;
2) store your arrays in a central object and return the name of the property that you found it in
var arrs = {
'one': ['foo', 'bar']
/* two, three etc */
};
for(var i in arrs)
if (arrs[i].indexOf('foo') != -1)
return i;
When you have a group of things, store them in an array (if they are ordered) or an object (if they are named). If you spot a bunch of variables with almost identical names, you're probably doing something wrong.
var things = [
['a','b','c','d'],
['e','f','g','h'],
['i','j','k','l']
];
Then you can loop over them:
for (var i = 0; i < things.length; i++) {
var thing = things[i];
if (thing.indexOf(id) > -1) { // indexOf returns -1 if not found, and the index (starting from 0) if it is.
return i;
}
}
return null;
var arr1 = ['a', 'b', 'c', 'd'];
var arr2 = ['e', 'f', 'g', 'h'];
var arr3 = ['i', 'j', 'k', 'l'];
function chkElem(elem) {
var isFound;
(isFound = arr1).indexOf(elem) > -1 || (isFound = arr2).indexOf(elem) > -1 || (isFound = arr3).indexOf(elem) > -1;
return isFound;
}
alert(chkElem('f'));