Trying to shorten redundant Javascript code - javascript

This is a working javascript code. However, it looks redundant to me. Is there any way to clean this up?
let text = 'Some search text';
const searchMatch =
entry.title.toLowerCase().includes(text.toLowerCase()) ||
entry.description.toLowerCase().includes(text.toLowerCase()) ||
entry.keywords.toLowerCase().includes(text.toLowerCase());
return searchMatch;

You could do something like this:
const text = 'Some search text'.toLowerCase();
return [entry.title, entry.description, entry.keywords].some(s => s.toLowerCase().includes(text));

You might use an array and a .some test instead:
const textLower = text.toLowerCase();
return ['title', 'description', 'keywords']
.map(prop => entry[prop].toLowerCase())
.some(s => s.includes(textLower));
If, by chance, entry contains only those properties, then you could use Object.values instead:
return Object.values(entry)
.map(s => s.toLowerCase())
.some(s => s.includes(textLower));

You could just use a one-line return statement involving an array composed of entry.description, entry.keywords and entry.title, and then using Array.prototype.some() to return a Boolean (true/false) value depending on whether any of the tests pass:
return [entry.description, entry.keywords, entry.title].some(string => string.toLowerCase().includes('Some search text'.toLowerCase());
Here's essentially a breakdown of each part:
[entry.description, entry.keywords, entry.title].some(...)
What this does is makes an anonymous array composed of entry.description, entry.keywords, and entry.title (the order does not matter) and iterates through it with the Array.prototype.some() method. According to the MDN page, .some():
The some() method tests whether at least one element in the array passes the test implemented by the provided function.
Essentially iterates through each element, and depending on the callback from the provided function, and provides a Boolean value (true if at least one element in the array passes the test, false if no elements pass the test).
string => string.toLowerCase().includes('Some search text'.toLowerCase())
This is the anonymous function contained within the .some() method, and it takes a single parameter string. Then it returns a Boolean value, depending on the outcome of the .includes() method. The .includes() method returns another Boolean value, depending on whether the lowercased string contains the lowercased 'Some search text'. It's a mouthful, but in a nutshell, the line of code above reads:
If string in lowercased form includes 'Some search text' in lowercased form, return true - otherwise, return false.
Hopefully this helps you!

Related

Using maps over template literals

so I need to print a boolean value if the given string contains [[placeholder:var3]] but the var3 will be dynamically fed from an array as belwo:
const delmethod = ['all', 'email', 'sms', 'fax', 'voice', 'voicemail', 'pager', 'pagerTwoWay'];
let languages = organizationLocales.map(a => a.locale);
let variabless = alertDetails.variables?.map(k => k.name); languages && languages.length > 0 && languages.map(lang => { delmethod.map(i => {
if ( alertDetails.alertMessage?.[${lang}]?.[i]?.variabless?.some(el => [i] === [[placeholder:${el}]]) )
{ bodyContainsVariables = true; } }); })
I tried using map around the template literals but it is throwing me an error also tried like above eg but it checks only first value of the array, so can someone please help me solve this, I'd appreciate any help, thanks in advance.
updating the question with the actual PS. organizationLocales is an array and alertDetails is an object that has an variables array, Delmethod is an array is being used to check the different properties dynamically
[[placeholder:${variabless}]] yields [[placeholder:var1,var2,var3]]. It doesn't check the first variable. And that's not how Array.string.includes is supposed to be used. For checking exact match use the === operator.
In case you want to check if any of the array elements match the string you can use the Array.prototype.some method which returns a boolean value.
let any = variabless.some(el => s === `[[placeholder:${el}]]`)
If you want to find the element that matches with the string use the Array.prototype.find function.
You need to tell the index inside template literals becasue variabless is an array like this:
console.log(s.includes(`[[placeholder:${variabless[2]}]]`));
I hope it helps you.
To match against any value in variabless, you can test against a regular expression:
console.log(new RegExp(
`\\[\\[placeholder:(${variabless.join('|')})\\]\\]`
).test(s));
you're currently passing the whole array in
s.includes([[placeholder:${variabless}]])
and that's won't work, you need to specify which elemnt of the variables array you want to inject to your string which is element indexed with 2 so it should be like that
s.includes([[placeholder:${variabless[2]}]])

Confusion with how indexOf works in JS

I am new to JS and was trying to learn how to properly work with indexOf in JS, that is, if you look at the code below:
var sandwiches = ['turkey', 'ham', 'turkey', 'tuna', 'pb&j', 'ham', 'turkey', 'tuna'];
var deduped = sandwiches.filter(function (sandwich, index) {
return sandwiches.indexOf(sandwich) === index;
});
// Logs ["turkey", "ham", "tuna", "pb&j"]
console.log(deduped);
I am trying to remove duplicates but wanted to ask two questions. Firstly, in here return sandwiches.indexOf(sandwich) === index; why we need to use "== index;". Secondly, since indexOf returns index like 0, 1 or 2 ... then why when we console.log(deduped) we get array of names instead of array of indexes. Hope you got my points
You use a method of Javascript Array that is filter, this method take a function that returns a boolean.
The function filter returns a new Array based on the function passed applied to each entry.
If the function return true, then the entry is included in the new Array, otherwise is discarded.
As the functions check the indexOf an entry to be the current index is true for the first occurrency of the entry.
All the duplications will fail the expression as they are not the first index found by indexOf, so they are discarded.
since the logic is to remove the duplicates from the array,
in your example, you have "turkey" as duplicates.
the "turkey" exists in position 0,2,6
so whenever you call indexOf("turkey") always returns 0 because the indexOf function returns the first occurrence of a substring.
so for the elements in position 2 & 6 the condition fails. then it won't return that element.
That is how the filter works in javascript. it evaluates the condition and returns true or false that indicates whether an element to be included in the new array or not, in your example the condition is return sandwiches.indexOf(sandwich) === index;
Perhaps the basic logic is easier to see at a glance if you use arrow notation:
const deduped = myArray => myArray.filter((x, i) => myArray.indexOf(x) === i);
The key point is that indexOf returns the index of the first occurrence of x. For that occurrence the result of the comparison will be true hence the element will be retained by the filter. For any subsequent occurrence the comparison will be false and the filter will reject it.
Difference between === (identity) and == (equality): if type of compared values are different then === will return false, while == will try to convert values to the same type. So, in cases where you compare some values with known types it is better to use ===. (http://www.c-point.com/javascript_tutorial/jsgrpComparison.htm)
You get as result an array of names instead of array of indexes because Array.filter do not change the values, but only filter them. The filter function in your case is return sandwiches.indexOf(sandwich) === index; which return true or false. If you want get the indexes of your items after deduplication, then use map after filter:
a.filter(...).map(function(item, idx) {return idx;})
#Dikens, indexOf gives the index of the element if found. And if the element is not found then it returns -1.
In your case you are filtering the array and storing the values in the deduped. That's why it is showing an array.
If you console the indexOf in the filter function then it will log the index of the element.
For example :
var deduped = sandwiches.filter(function (sandwich, index) {
console.log(sandwiches.indexOf(sandwich));
return sandwiches.indexOf(sandwich) === index;
});

Mysql Like statement in javascript with array of objects?

I am pretty confused with relative searching in javascript For example
let array = [{name:'ram'},{name:'kumar ra'},{name:'nani'}]
If i search ra then array should be like [{name:'ram'},{name:'kumar ra'}]Similar to mysql Like statement .But i don't know how to do it in javascript ..
Thanks in advance..
Arrays have a filter (Array.prototype.filter) method which behaves very much like a where clause.
It takes a predicate and returns a new array containing the elements that satisfy that predicate.
const array = [{
name: 'ram'
}, {
name: 'kumar ra'
}, {
name: 'nani'
}];
const filtered = array.filter(e => e.name.match(/ra/));
console.log(filtered);
In the predicate itself, we use String's match (String.prototype.match) method to test each name against a regular expression. This is conceptually similar to a like expression in SQL.
The filter method walks the array and calls the predicate on each element of it. I have named this element e in inside the predicate. When the filter method calls the predicate, it passes the current element as the first argument thus binding it to e.
you can apply filter method on array elements to find items that their names contains 'ra'
let result=array.filter((item)=>{return item.name.contains('ra')})

When to use forEach vs map when I am just iterating over something?

I know that map returns a new array, and that forEach does not return anything (the docs say it returns undefined).
For example, if I had some code like this:
let test;
values.forEach((value, idx) => {
if (someNumber >= value) {
test = value;
}
});
Here I am just checking if someNumber is greater than some value, and if it is then set test = value. Is there another array method I should use here?
Or is it fine to use .forEach
Your example doesn't make sense because it finds the last value that is less than or equal to someNumber, repeatedly assigning to the test variable if more than one is found. Thus, your code is not truly expressing your intent well since other developers can be confused about what you're trying to achieve. In fact, other answers here have had differing opinions on your goal due to this ambiguity. You even said:
if the number is great than or equal to the value from the array at whatever index, stop, and set test equal to that value
But your code doesn't stop at the first value! It keeps going through the entire array and the result in test will be the last value, not the first one.
In general, making your loop refer to outside variables is not the best way to express your intent. It makes it harder for the reader to understand what you're doing. It's better if the function you use returns a value so that it's clear the variable is being assigned.
Here's a guide for you:
forEach
Use this when you want to iterate over all the values in order to do something with each of them. Don't use this if you are creating a new output value--but do use it if you need to modify existing items or run a method on each one, where the forEach has no logical output value. array.forEach at MDN says:
There is no way to stop or break a forEach() loop other than by throwing an exception. If you need such behavior, the forEach() method is the wrong tool, use a plain loop instead. If you are testing the array elements for a predicate and need a Boolean return value, you can use every() or some() instead. If available, the new methods find() or findIndex() can be used for early termination upon true predicates as well.
find
Use this when you want to find the first instance of something, and stop. What you said makes it sound like you want this:
let testResult = values.find(value => value <= someNumber);
This is far superior to setting the test value from inside the lambda or a loop. I also think that reversing the inequality and the variables is better because of the way we tend to think about lambdas.
some
These only give you a Boolean as a result, so you have to misuse them slightly to get an output value. It will traverse the array until the condition is true or the traversal is complete, but you have to do something a bit hacky to get any array value out. Instead, use find as above, which is intended to output the found value instead of simply a true/false whether the condition is met by any element in the array.
every
This is similar to some in that it returns a Boolean, but is what you would expect, it is only true if all the items in the array meet the condition. It will traverse the array until the condition is false or the traversal is complete. Again, don't misuse it by throwing away the Boolean result and setting a variable to a value. If you want to do something to every item in an array and return a single value, at that point you would want to use reduce. Also, notice that !arr.every(lambdacondition) is the same as arr.some(!lambdacondition).
reduce
The way your code is actually written—finding the last value that matches the condition—naturally lends itself to reduce:
let testResult = values.reduce(
(recent, value) => {
if (value <= someNumber) {
recent = value;
}
return recent;
},
undefined
);
This does the same job of finding the last value as your example code does.
map
map is for when you want to transform each element of an array into a new array of the same length. If you have any experience with C# it is much like the Linq-to-objects .Select method. For example:
let inputs = [ 1, 2, 3, 4];
let doubleInputs = inputs.map(value => value * 2);
// result: [ 2, 4, 6, 8]
New requirements
Given your new description of finding the adjacent values in a sorted array between which some value can be found, consider this code:
let sortedBoundaries = [ 10, 20, 30, 40, 50 ];
let inputValue = 37;
let interval = sortedBoundaries
.map((value, index) => ({ prev: value, next: sortedBoundaries[index + 1] }))
.find(pair => pair.prev < inputValue && inputValue <= pair.next);
// result: { prev: 20, next: 30 }
You can improve this to work on the ends so that a number > 50 or <= 10 will be found as well (for example, { prev: undefined, next: 10 }).
Final notes
By using this coding style of returning a value instead of modifying an outside variable, you not only communicate your intent better to other developers, you then get the chance to use const instead of let if the variable will not be reassigned afterward.
I encourage you to browse the documentation of the various Array prototype functions at MDN—doing this will help you sort them out. Note that each method I listed is a link to the MDN documentation.
I would suggest you to use Array#some, instead of Array#forEach.
Array#forEach keeps iterating the array even if given condition was fulfilled.
Array#some stops iteration when given condition was fulfilled.
One of the advantages would be connected with performance, another - depends on your purposes - Array#forEach keeps overwriting the result with every passed condition, Array#some assigns the first found value and stops the iteration.
let test,
values = [4,5,6,7],
someNumber = 5;
values.some((value, idx) => {
if (someNumber >= value) {
test = value;
return test;
}
});
console.log(test);
Another option would be to use the Array.some() method.
let test;
const someNumber = 10;
[1, 5, 10, 15].some(function (value) {
if (value > someNumber) {
return test = value
}
})
One advantage to the .some() method over your original solution is optimization, as it will return once the condition has been met.
How about Object?
you can search with for-of

use lodash to find substring from array of strings

I'm learning lodash. Is it possible to use lodash to find a substring in an array of strings?
var myArray = [
'I like oranges and apples',
'I hate banana and grapes',
'I find mango ok',
'another array item about fruit'
]
is it possible to confirm if the word 'oranges' is in my array?
I've tried _.includes, _.some, _.indexOf but they all failed as they look at the full string, not a substring
You can easily construct an iteratee for some() using lodash's higher-order functions. For example:
_.some(myArray, _.unary(_.partialRight(_.includes, 'orange')));
The unary() function ensures that only one argument is passed to the callback. The partialRight() function is used to apply the 'orange' value as the second argument to includes(). The first argument is supplied with each iteration of some().
However, this approach won't work if case sensitivity matters. For example, 'Orange' will return false. Here's how you can handle case sensitivity:
_.some(myArray, _.method('match', /Orange/i));
The method() function creates a function that will call the given method of the first argument passed to it. Here, we're matching against a case-insensitive regular expression.
Or, if case-sensitivity doesn't matter and you simply prefer the method() approach, this works as well for ES2015:
_.some(myArray, _.method('includes', 'orange'));
Two quick ways to do it - neither uses lodash (sorry)
var found = myArray.filter(function(el){
return el.indexOf('oranges') > -1;
}).length;
if (found) { // oranges was found }
or as I mentioned in the comment:
var found = myArray.join(',').indexOf('oranges') > -1;
if (found) { // oranges was found }
You can do this using lodash, but it's also very doable using native javascript methods:
function stringArrayContains(array, str) {
function contains(el) {
return (el.indexOf(str) !== -1) ? true : false;
}
return array.some(contains);
}
Testing the above function:
var a = ['hello', 'there'];
var b = ['see', 'ya', 'later'];
stringArrayContains(a, 'ell') // true
stringArrayContains(a, 'what') // false
stringArrayContains(b, 'later') // true
stringArrayContains(b, 'hello') // false
Array.prototype.some applies a function you define to every element of an array. This function (named contains in our case) must return true or false. While iterating through the array elements, if any of the elements returns true, the some method returns true.
Personally, I think in general that if you can use native JS methods for simple functions, it's preferable to loading an library just to do the same thing. Lodash absolutely does have performance benefits, but they aren't necessarily realized unless you're processing large amounts of data. Just my two cents.
Cheers!
The best way is to define a function to check the inclusion of a substring.
var contains = _.curry(function (substring, source) {
return source.indexOf(substring) !== -1;
});
I use _.curry here to get a curried function, which can be partially applied then.
_.some(myArray, contains('item'));
You can also find a substring in a joined string.
contains('item', _.join(myArray))
UPD:
I have not noticed that lodash already has a function to find value in a collection.
The function _.includes is quite the same to what I defined above. However, as everything in lodash, it uses the different order for arguments. In my example, I put a source as the latest argument for a curried function which makes my function useful for point-free style programming when lodash waits for the source as a first argument of the same function.
Check the Brian Lonsdorf's talk on this matter https://www.youtube.com/watch?v=m3svKOdZijA
Also take a chance to look into ramda. This library provides a better way for practical functional programming in JavaScript.
I ran into this Question / Answer thread while trying to figure out how to match a substring against each String in an Array and REMOVE any array item that contains that substring.
While the above answers put me on track, and while this doesn't specifically answer the original question, this thread DOES appear first in the google search when you are trying to figure out how to accomplish the above removal of an array item so I figured I would post an answer here.
I ended up finding a way to use Lodash's _.remove function to remove matching array strings as follows:
// The String (SubString) we want to match against array (for dropping purposes)
var searchSubString = "whatever"
// Remove all array items that contain the text "whatever"
_.remove(my_array, function(searchSubString) {
return n.indexOf(searchSubString) !== -1;
});
Basically indexOf is matching against the position of the substring within the string, if the substring is not found it will return -1, when indexOf returns a number other than -1 (the number is the SubString position in number of characters within the Array string).
Lodash removes that Array item via array mutation and the newly modified array can be accessed by the same name.
_.some(myArray, function(str){
return _.includes(str, 'orange')
})
let str1 = 'la rivière et le lapin sont dans le près';
let str2 = 'product of cooking class';
let str3 = 'another sentence to /^[analyse]/i with weird!$" chars#';
_.some(_.map(['rabbit','champs'], w => str1.includes(w)), Boolean), // false
_.some(_.map(['cook'], w => str2.includes(w)), Boolean), // true
_.some(_.map(['analyse'], w => str3.includes(w)), Boolean), // true

Categories

Resources