如何在数组中搜索子字符串匹配?

我需要用 JavaScript 搜索一个数组。搜索将只搜索与之匹配的字符串的一部分,因为该字符串将包含其他组件。然后需要返回成功匹配的带有完整字符串的数组元素。

例如:

const windowArray = [ "item", "thing", "id-3-text", "class" ];

我需要搜索包含 "id-"的数组元素,还需要提取元素中其余的文本(即 "id-3-text")。

我该怎么做?

201060 次浏览

In your specific case, you can do it just with a boring old counter:

var index, value, result;
for (index = 0; index < windowArray.length; ++index) {
value = windowArray[index];
if (value.substring(0, 3) === "id-") {
// You've found it, the full text is in `value`.
// So you might grab it and break the loop, although
// really what you do having found it depends on
// what you need.
result = value;
break;
}
}


// Use `result` here, it will be `undefined` if not found

But if your array is sparse, you can do it more efficiently with a properly-designed for..in loop:

var key, value, result;
for (key in windowArray) {
if (windowArray.hasOwnProperty(key) && !isNaN(parseInt(key, 10))) {
value = windowArray[key];
if (value.substring(0, 3) === "id-") {
// You've found it, the full text is in `value`.
// So you might grab it and break the loop, although
// really what you do having found it depends on
// what you need.
result = value;
break;
}
}
}


// Use `result` here, it will be `undefined` if not found

Beware naive for..in loops that don't have the hasOwnProperty and !isNaN(parseInt(key, 10)) checks; here's why.


Off-topic:

Another way to write

var windowArray = new Array ("item","thing","id-3-text","class");

is

var windowArray = ["item","thing","id-3-text","class"];

...which is less typing for you, and perhaps (this bit is subjective) a bit more easily read. The two statements have exactly the same result: A new array with those contents.

Another possibility is

var res = /!id-[^!]*/.exec("!"+windowArray.join("!"));
return res && res[0].substr(1);

that IMO may make sense if you can have a special char delimiter (here i used "!"), the array is constant or mostly constant (so the join can be computed once or rarely) and the full string isn't much longer than the prefix searched for.

ref: In javascript, how do you search an array for a substring match

The solution given here is generic unlike the solution 4556343#4556343, which requires a previous parse to identify a string with which to join(), that is not a component of any of the array strings.
Also, in that code /!id-[^!]*/ is more correctly, /![^!]*id-[^!]*/ to suit the question parameters:

  1. "search an array ..." (of strings or numbers and not functions, arrays, objects, etc.)
  2. "for only part of the string to match " (match can be anywhere)
  3. "return the ... matched ... element" (singular, not ALL, as in "... the ... elementS")
  4. "with the full string" (include the quotes)

... NetScape / FireFox solutions (see below for a JSON solution):

javascript:         /* "one-liner" statement solution */
alert(
["x'!x'\"id-2",'\' "id-1 "',   "item","thing","id-3-text","class" ] .
toSource() . match( new RegExp(
'[^\\\\]("([^"]|\\\\")*' + 'id-' + '([^"]|\\\\")*[^\\\\]")' ) ) [1]
);

or

javascript:
ID = 'id-' ;
QS = '([^"]|\\\\")*' ;           /* only strings with escaped double quotes */
RE = '[^\\\\]("' +QS+ ID +QS+ '[^\\\\]")' ;/* escaper of escaper of escaper */
RE = new RegExp( RE ) ;
RA = ["x'!x'\"id-2",'\' "id-1 "',   "item","thing","id-3-text","class" ] ;
alert(RA.toSource().match(RE)[1]) ;

displays "x'!x'\"id-2".
Perhaps raiding the array to find ALL matches is 'cleaner'.

/* literally (? backslash star escape quotes it!) not true, it has this one v  */
javascript:                            /* purely functional - it has no ... =! */
RA = ["x'!x'\"id-2",'\' "id-1 "',   "item","thing","id-3-text","class" ] ;
function findInRA(ra,id){
ra.unshift(void 0) ;                                     /* cheat the [" */
return ra . toSource() . match( new RegExp(
'[^\\\\]"' + '([^"]|\\\\")*' + id + '([^"]|\\\\")*' + '[^\\\\]"' ,
'g' ) ) ;
}
alert( findInRA( RA, 'id-' ) . join('\n\n') ) ;

displays:

"x'!x'\"id-2"


"' \"id-1 \""


"id-3-text"

Using, JSON.stringify():

javascript:                             /* needs prefix cleaning */
RA = ["x'!x'\"id-2",'\' "id-1 "',   "item","thing","id-3-text","class" ] ;
function findInRA(ra,id){
return JSON.stringify( ra ) . match( new RegExp(
'[^\\\\]"([^"]|\\\\")*' + id + '([^"]|\\\\")*[^\\\\]"' ,
'g' ) ) ;
}
alert( findInRA( RA, 'id-' ) . join('\n\n') ) ;

displays:

["x'!x'\"id-2"


,"' \"id-1 \""


,"id-3-text"

wrinkles:

  • The "unescaped" global RegExp is /[^\]"([^"]|\")*id-([^"]|\")*[^\]"/g with the \ to be found literally. In order for ([^"]|\")* to match strings with all "'s escaped as \", the \ itself must be escaped as ([^"]|\\")*. When this is referenced as a string to be concatenated with id-, each \ must again be escaped, hence ([^"]|\\\\")*!
  • A search ID that has a \, *, ", ..., must also be escaped via .toSource() or JSON or ... .
  • null search results should return '' (or "" as in an EMPTY string which contains NO "!) or [] (for all search).
  • If the search results are to be incorporated into the program code for further processing, then eval() is necessary, like eval('['+findInRA(RA,ID).join(',')+']').

--------------------------------------------------------------------------------

Digression:
Raids and escapes? Is this code conflicted?
The semiotics, syntax and semantics of /* it has no ... =! */ emphatically elucidates the escaping of quoted literals conflict.

Does "no =" mean:

  • "no '=' sign" as in javascript:alert('\x3D') (Not! Run it and see that there is!),
  • "no javascript statement with the assignment operator",
  • "no equal" as in "nothing identical in any other code" (previous code solutions demonstrate there are functional equivalents),
  • ...

Quoting on another level can also be done with the immediate mode javascript protocol URI's below. (// commentaries end on a new line (aka nl, ctrl-J, LineFeed, ASCII decimal 10, octal 12, hex A) which requires quoting since inserting a nl, by pressing the Return key, invokes the URI.)

javascript:/* a comment */  alert('visible')                                ;
javascript:// a comment ;   alert(  'not'  ) this is all comment             %0A;
javascript:// a comment %0A alert('visible but  %\0A  is wrong ')   // X     %0A
javascript:// a comment %0A alert('visible but %'+'0A is a pain to type')   ;

Note: Cut and paste any of the javascript: lines as an immediate mode URI (at least, at most?, in FireFox) to use first javascript: as a URI scheme or protocol and the rest as JS labels.

For a fascinating examination of some of the alternatives and their efficiency, see John Resig's recent posts:

(The problem discussed there is slightly different, with the haystack elements being prefixes of the needle and not the other way around, but most solutions are easy to adapt.)

If you're able to use Underscore.js in your project, the _.filter() array function makes this a snap:

// find all strings in array containing 'thi'
var matches = _.filter(
[ 'item 1', 'thing', 'id-3-text', 'class' ],
function( s ) { return s.indexOf( 'thi' ) !== -1; }
);

The iterator function can do whatever you want as long as it returns true for matches. Works great.

Update 2017-12-03:
This is a pretty outdated answer now. Maybe not the most performant option in a large batch, but it can be written a lot more tersely and use native ES6 Array/String methods like .filter() and .includes() now:

// find all strings in array containing 'thi'
const items = ['item 1', 'thing', 'id-3-text', 'class'];
const matches = items.filter(s => s.includes('thi'));

Note: There's no <= IE11 support for String.prototype.includes() (Edge works, mind you), but you're fine with a polyfill, or just fall back to indexOf().

The simplest vanilla javascript code to achieve this is

var windowArray = ["item", "thing", "id-3-text", "class", "3-id-text"];
var textToFind = "id-";


//if you only want to match id- as prefix
var matches = windowArray.filter(function(windowValue){
if(windowValue) {
return (windowValue.substring(0, textToFind.length) === textToFind);
}
}); //["id-3-text"]


//if you want to match id- string exists at any position
var matches = windowArray.filter(function(windowValue){
if(windowValue) {
return windowValue.indexOf(textToFind) >= 0;
}
}); //["id-3-text", "3-id-text"]

Just search for the string in plain old indexOf

arr.forEach(function(a){
if (typeof(a) == 'string' && a.indexOf('curl')>-1) {
console.log(a);
}
});

People here are making this waaay too difficult. Just do the following...

myArray.findIndex(element => element.includes("substring"))

findIndex() is an ES6 higher order method that iterates through the elements of an array and returns the index of the first element that matches some criteria (provided as a function). In this case I used ES6 syntax to declare the higher order function. element is the parameter of the function (which could be any name) and the fat arrow declares what follows as an anonymous function (which does not need to be wrapped in curly braces unless it takes up more than one line).

Within findIndex() I used the very simple includes() method to check if the current element includes the substring that you want.

Here's your expected snippet which gives you the array of all the matched values -

var windowArray = new Array ("item","thing","id-3-text","class");


var result = [];
windowArray.forEach(val => {
if(val && val.includes('id-')) {
result.push(val);
}
});


console.log(result);

let url = item.product_image_urls.filter(arr=>arr.match("homepage")!==null)

Filter array with string match. It is easy and one line code.

I think this may help you. I had a similar issue. If your array looks like this:

var array = ["page1","1973","Jimmy"];

You can do a simple "for" loop to return the instance in the array when you get a match.

var c;
for (i = 0; i < array.length; i++) {
if (array[i].indexOf("page") > -1){
c = i;}
}

We create an empty variable, c to host our answer. We then loop through the array to find where the array object (e.g. "page1") matches our indexOf("page"). In this case, it's 0 (the first result)

Happy to expand if you need further support.

this worked for me .

    const filterData = this.state.data2.filter(item=>((item.name.includes(text)) || (item.surname.includes(text)) || (item.email.includes(text)) || (item.userId === Number(text))) ) ;

The simplest way to get the substrings array from the given array is to use filter and includes:

myArray.filter(element => element.includes("substring"));

The above one will return an array of substrings.

myArray.find(element => element.includes("substring"));

The above one will return the first result element from the array.

myArray.findIndex(element => element.includes("substring"));

The above one will return the index of the first result element from the array.

Use this function for search substring Item.

function checkItem(arrayItem, searchItem) {
return arrayItem.findIndex(element => element.includes(searchItem)) >= 0
}


function getItem(arrayItem, getItem) {
return arrayItem.filter(element => element.includes(getItem))
}


var arrayItem = ["item","thing","id-3-text","class"];




console.log(checkItem(arrayItem, "id-"))
console.log(checkItem(arrayItem, "vivek"))
console.log(getItem(arrayItem, "id-"))

I've created a simple to use library (ss-search) which is designed to handle objects, but could also work in your case:

search(windowArray.map(x => ({ key: x }), ["key"], "SEARCH_TEXT").map(x => x.key)

The advantage of using this search function is that it will normalize the text before executing the search to return more accurate results.