{"id":3882,"date":"2020-12-03T07:22:00","date_gmt":"2020-12-03T12:22:00","guid":{"rendered":"https:\/\/blogs.mathworks.com\/loren\/?p=3882"},"modified":"2020-12-10T17:09:36","modified_gmt":"2020-12-10T22:09:36","slug":"stressed-when-searching-for-strings","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/loren\/2020\/12\/03\/stressed-when-searching-for-strings\/","title":{"rendered":"Stressed When Searching for Strings?"},"content":{"rendered":"<div class=\"content\"><!--introduction--><p>Do you get clammy hands when you have to search for a string pattern, not just a particular string?  Does the thought of struggling with <tt><a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/regexp.html\">regexp<\/a><\/tt> make you sweat?<\/p><p>Well worry no more!  Many of your searches may now be done more easily using the new <tt><a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/pattern.html\">pattern<\/a><\/tt> feature in MATLAB.  And in some cases, you can get away with even less.<\/p><p>For today's post, my co-authors are Jason Breslau and Curtis Anderson, since they know MUCH more about <tt>regexp<\/tt> than me, and many more nuances about the functionality.  We're going to do this by showcasing a few examples.  You might also want to check out Jiro's recent <a href=\"https:\/\/blogs.mathworks.com\/pick\/2020\/10\/15\/r2020b-pattern-new-way-to-regular-express\">Pick of the Week<\/a>.<\/p><!--\/introduction--><h3>Contents<\/h3><div><ul><li><a href=\"#a1486765-4226-4b74-8be0-be7fd0dd7958\">Example 0: For Those Who Love regexp<\/a><\/li><li><a href=\"#c620b1bd-da42-4c96-8338-486f91270b6e\">Example 1: Counting Comment Lines<\/a><\/li><li><a href=\"#4dbe8adb-55b8-4e20-b722-5badd9240234\">Example 2: How to Find Words in a File Starting with a Vowel<\/a><\/li><li><a href=\"#fb1583ea-8876-4622-8b70-0847a45ba1da\">Example 3: Converse - Find Words Beginning with Consonants<\/a><\/li><li><a href=\"#f603c121-afa3-4a03-bf19-e8b871386e9e\">Example 4: Looking for Files with Certain Extensions<\/a><\/li><li><a href=\"#3ddc84e2-6134-4d7d-ba55-a9b2dca56799\">What about a File with No Extension<\/a><\/li><li><a href=\"#57281231-3dda-4f90-afab-23fee6a84bf5\">How's Your Search Going?<\/a><\/li><li><a href=\"#6f74e413-1392-4158-befc-c6c6aa8567c4\">Appendix<\/a><\/li><\/ul><\/div><h4>Example 0: For Those Who Love regexp<a name=\"a1486765-4226-4b74-8be0-be7fd0dd7958\"><\/a><\/h4><p>This example is for you if you love <tt>regexp<\/tt> and don't see why you should consider anything else.  You can use <tt>regexpPattern<\/tt> to convert your favorite regular expression to a pattern so you can take advantages of code features.  Compare these two ways to see if a string is contained in some text.<\/p><pre>               contains(str, regexpPattern(expr))<\/pre><pre>               ~cellfun('isempty',regexp(str,expr))<\/pre><p>Which one of these can you quickly understand without having to go through the logic each time you read it?<\/p><p>And now more for those who really would prefer to skip <tt>regexp<\/tt> more often.<\/p><h4>Example 1: Counting Comment Lines<a name=\"c620b1bd-da42-4c96-8338-486f91270b6e\"><\/a><\/h4><p>Suppose I want to count the lines in a MATLAB file that are comments, (not block comments). Here's how to do this with <tt>regexp<\/tt>:<\/p><pre class=\"codeinput\">codeFile = fileread(<span class=\"string\">'num2str.m'<\/span>);\r\ncomments = regexp(codeFile, <span class=\"string\">'^\\s*%'<\/span>, <span class=\"string\">'lineanchors'<\/span>);\r\nnumel(comments)\r\n<\/pre><pre class=\"codeoutput\">ans =\r\n    37\r\n<\/pre><p>That's annoying as you had to know about lineanchors, and that regular expression is a little ugly. Plus, it returned an array of indices, that we don't really care about. Instead, try this:<\/p><pre class=\"codeinput\">count(codeFile, lineBoundary + whitespacePattern + <span class=\"string\">\"%\"<\/span>)\r\n<\/pre><pre class=\"codeoutput\">ans =\r\n    37\r\n<\/pre><p>We still need to read the file, and we look for lines (ignoring leading whitespace), that effectively start with %.<\/p><h4>Example 2: How to Find Words in a File Starting with a Vowel<a name=\"4dbe8adb-55b8-4e20-b722-5badd9240234\"><\/a><\/h4><p>Suppose we want to get some statistics about words starting with a vowel.<\/p><pre class=\"codeinput\">vowelWords = regexpi(codeFile, <span class=\"string\">'\\&lt;[aeiou][a-z]*'<\/span>, <span class=\"string\">'match'<\/span>);\r\nhowManyWords = length(vowelWords)\r\n<\/pre><pre class=\"codeoutput\">howManyWords =\r\n   176\r\n<\/pre><p>Using <tt>pattern<\/tt>, we first search for words, which are alphabetic characters.  And then look only for the ones starting with a vowel.<\/p><pre class=\"codeinput\">words = extract(codeFile, lettersPattern);\r\nvowelWords1 = words(startsWith(words, characterListPattern(<span class=\"string\">'aeiou'<\/span>),<span class=\"string\">'IgnoreCase'<\/span>, true));\r\nhowManyWords = length(vowelWords1)\r\n<\/pre><pre class=\"codeoutput\">howManyWords =\r\n   176\r\n<\/pre><p>And here's perhaps an even better way to do this!  Build a pattern from a list of the vowels.  And then look for something that has a boundary before a letter - some whitespace, followed by a vowel and then for the possible rest of the word.<\/p><pre class=\"codeinput\">vowel = caseInsensitivePattern(characterListPattern(<span class=\"string\">\"aeiou\"<\/span>));\r\nvowelWords2Pat = letterBoundary + vowel + lettersPattern(0,inf);\r\nvowelWords2 = extract(codeFile, vowelWords2Pat);\r\nhowManyWords = length(vowelWords2)\r\n<\/pre><pre class=\"codeoutput\">howManyWords =\r\n   176\r\n<\/pre><p>Assuming we <i>only<\/i> want the count here, we can replace the previous last 2 lines of code with a call to <tt>count<\/tt> and more efficiently attain our goal.  The great thing about the workflow of building up the complex pattern is the versatility it affords you.<\/p><pre class=\"codeinput\">hmw = count(codeFile,vowelWords2Pat,<span class=\"string\">\"IgnoreCase\"<\/span>, true)\r\n<\/pre><pre class=\"codeoutput\">hmw =\r\n   176\r\n<\/pre><p>Or I could replace <tt>lettersPattern(0,Inf)<\/tt> with <tt>optionalPattern(lettersPattern)<\/tt>. Being able to give patterns to functions like <tt>count<\/tt>, <tt>startsWith<\/tt> and <tt>contains<\/tt> is the biggest win.<\/p><p><i>Best Practice<\/i><\/p><p>We have found that it is best to build up a pattern by joining smaller pieces.  It makes it easier to understand what you are doing, where you are or are not applying case sensitivity, etc.<\/p><h4>Example 3: Converse - Find Words Beginning with Consonants<a name=\"fb1583ea-8876-4622-8b70-0847a45ba1da\"><\/a><\/h4><p>Suppose instead we want words starting with consonants.  Here's the <tt>regexp<\/tt> way.<\/p><pre class=\"codeinput\">consRegexp = regexpi(codeFile, <span class=\"string\">'\\&lt;(?![aeiou])[a-z]+'<\/span>, <span class=\"string\">'match'<\/span>);\r\n<\/pre><p>And using a pattern<\/p><pre class=\"codeinput\">consPat = extract(codeFile, <span class=\"keyword\">...<\/span>\r\n    alphanumericBoundary + <span class=\"keyword\">...<\/span>\r\n    ~lookAheadBoundary(caseInsensitivePattern(characterListPattern(<span class=\"string\">'aeiou'<\/span>)))<span class=\"keyword\">...<\/span>\r\n    + lettersPattern);\r\n<\/pre><p>And finally using neither <tt>regexp<\/tt> nor a consonant pattern.  Instead, use the negation of the starting with vowel words. This is the easiest to understand, perhaps.<\/p><pre class=\"codeinput\">consWords = words(~startsWith(words, caseInsensitivePattern(characterListPattern(<span class=\"string\">'aeiou'<\/span>))));\r\n<\/pre><p>The astute reader will see that the answers here do not agree.  See the Appendix at the end for details and how to get the answers to align.<\/p><h4>Example 4: Looking for Files with Certain Extensions<a name=\"f603c121-afa3-4a03-bf19-e8b871386e9e\"><\/a><\/h4><p>We audited all of the regular expressions used in one stage in our test system, and found that around 50% of them could be replaced by <tt>endsWith<\/tt> with NO PATTERN at all. Previously we used <tt>regexp<\/tt> but that is a huge hammer for the job. I think looking for files with a particular file extension may have been a common use case. like,<\/p><pre>               regexp(fileName, '.txt$')<\/pre><p>which has two bugs! You need <tt>isempty<\/tt>, and <tt>'once'<\/tt>:<\/p><pre>               ~isempty(regexp(fileName, '.txt$', 'once'))<\/pre><p>And you also have to escape the dot, which everyone forgets to do.<\/p><pre>               ~isempty(regexp(fileName, '\\.txt$', 'once'))<\/pre><p>Instead now you simply do<\/p><pre>               endsWith(fileName, '.txt')<\/pre><p>The interesting things is that this uses no pattern at all but uses a function, <tt>endsWith<\/tt>, that could take a pattern.<\/p><p>Suppose you now want to check for 2 different extensions. Easily done. <tt>endsWith<\/tt> supports multiple search strings, and treats them as an <tt>or<\/tt>. This is faster but a bit more limited than doing a search with a proper pattern.<\/p><pre>               endsWith(fileName, [\".txt\", \".somethingElse\"])<\/pre><p>pattern with explicit or<\/p><pre>               endsWith(fileName, \".txt\" | \".somethingElse\")<\/pre><h4>What about a File with No Extension<a name=\"3ddc84e2-6134-4d7d-ba55-a9b2dca56799\"><\/a><\/h4><p>What if a filename ends with either txt or no extension at all?<\/p><pre>               endsWith(fileName, '.txt') || ~contains(fileName, '.')<\/pre><p>This is for a single file, without a full pathname.<\/p><h4>How's Your Search Going?<a name=\"57281231-3dda-4f90-afab-23fee6a84bf5\"><\/a><\/h4><p>Are you able to make good use of patterns in MATLAB and are able (or not) to eliminate some or all uses of <tt>regexp<\/tt>.  Let us know <a href=\"https:\/\/blogs.mathworks.com\/loren\/?p=3882#respond\">here<\/a>.<\/p><h4>Appendix<a name=\"6f74e413-1392-4158-befc-c6c6aa8567c4\"><\/a><\/h4><p>As promised, I will describe here why the consonant answers do not agree and how to make them the same.<\/p><p>The <tt>words<\/tt> variable has groups of consecutive letters.  And we had some names in the <tt>num2str<\/tt> code using numbers as well, e.g., <tt>mat2str<\/tt>.  This translated into 2 words, <tt>mat<\/tt> and <tt>str<\/tt>.  We can fix this using<\/p><pre>               words = extract(codeFile, alphanumericBoundary ...\r\n                       + lettersPattern + alphanumericBoundary);<\/pre><p>This means the <tt>regexp<\/tt> version is:<\/p><pre>               consRegexp = regexpi(codeFile, '\\&lt;(?![aeiou])[a-z]+\\&gt;', 'match');<\/pre><p>and the corresponding pattern:<\/p><pre>               consPat = extract(codeFile, ...\r\n                 alphanumericBoundary + ...\r\n                 ~lookAheadBoundary(caseInsensitivePattern(characterListPattern('aeiou')))...\r\n                 + lettersPattern &#8230;\r\n                 + alphanumericBoundary);<\/pre><p>Phew! That's a mouthful!  But pretty readable too.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_a0408f98768044ccb3cf70c5a05b0399() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='a0408f98768044ccb3cf70c5a05b0399 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' a0408f98768044ccb3cf70c5a05b0399';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        copyright = 'Copyright 2020 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n\r\n        d.title = title + ' (MATLAB code)';\r\n        d.close();\r\n    }   \r\n     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_a0408f98768044ccb3cf70c5a05b0399()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2020b<br><\/p><\/div><!--\r\na0408f98768044ccb3cf70c5a05b0399 ##### SOURCE BEGIN #####\r\n%% Stressed When Searching for Strings?\r\n% Do you get clammy hands when you have to search for a string pattern, not\r\n% just a particular string?  Does the thought of struggling with\r\n% |<https:\/\/www.mathworks.com\/help\/matlab\/ref\/regexp.html regexp>| make you\r\n% sweat?\r\n%\r\n% Well worry no more!  Many of your searches may now be done more easily\r\n% using the new |<https:\/\/www.mathworks.com\/help\/matlab\/ref\/pattern.html\r\n% pattern>| feature in MATLAB.  And in some cases, you can get away with\r\n% even less.  \r\n%\r\n% For today's post, my co-authors are Jason Breslau and Curtis Anderson,\r\n% since they know MUCH more about |regexp| than me, and many more nuances\r\n% about the functionality.  We're going to do this by showcasing a few\r\n% examples.  You might also want to check out Jiro's recent\r\n% <https:\/\/blogs.mathworks.com\/pick\/2020\/10\/15\/r2020b-pattern-new-way-to-regular-express\r\n% Pick of the Week>.\r\n\r\n%% Example 0: For Those Who Love regexp\r\n% This example is for you if you love |regexp| and don't see why you should\r\n% consider anything else.  You can use |regexpPattern| to convert your\r\n% favorite regular expression to a pattern so you can take advantages of\r\n% code features.  Compare these two ways to see if a string is contained in\r\n% some text.\r\n%\r\n%                 contains(str, regexpPattern(expr))\r\n%\r\n%                 ~cellfun('isempty',regexp(str,expr))\r\n%\r\n% Which one of these can you quickly understand without having to go\r\n% through the logic each time you read it?\r\n%\r\n% And now more for those who really would prefer to skip |regexp| more\r\n% often.\r\n\r\n%% Example 1: Counting Comment Lines\r\n% Suppose I want to count the lines in a MATLAB file that are comments,\r\n% (not block comments). Here's how to do this with |regexp|:\r\ncodeFile = fileread('num2str.m');\r\ncomments = regexp(codeFile, '^\\s*%', 'lineanchors');\r\nnumel(comments)\r\n%%\r\n% That's annoying as you had to know about lineanchors, and that regular\r\n% expression is a little ugly. Plus, it returned an array of indices,\r\n% that we don't really care about. Instead, try this:\r\ncount(codeFile, lineBoundary + whitespacePattern + \"%\")\r\n%%\r\n% We still need to read the file, and we look for lines (ignoring leading\r\n% whitespace), that effectively start with %.\r\n\r\n%% Example 2: How to Find Words in a File Starting with a Vowel\r\n% Suppose we want to get some statistics about words starting with a vowel.\r\nvowelWords = regexpi(codeFile, '\\<[aeiou][a-z]*', 'match');\r\nhowManyWords = length(vowelWords)\r\n\r\n%% \r\n% Using |pattern|, we first search for words, which are alphabetic\r\n% characters.  And then look only for the ones starting with a vowel.\r\nwords = extract(codeFile, lettersPattern);\r\nvowelWords1 = words(startsWith(words, characterListPattern('aeiou'),'IgnoreCase', true));\r\nhowManyWords = length(vowelWords1)\r\n\r\n%% \r\n% And here's perhaps an even better way to do this!  Build a pattern from\r\n% a list of the vowels.  And then look for something that has a boundary\r\n% before a letter - some whitespace, followed by a vowel and then for the\r\n% possible rest of the word.\r\nvowel = caseInsensitivePattern(characterListPattern(\"aeiou\"));\r\nvowelWords2Pat = letterBoundary + vowel + lettersPattern(0,inf);\r\nvowelWords2 = extract(codeFile, vowelWords2Pat);\r\nhowManyWords = length(vowelWords2)\r\n\r\n%% \r\n% Assuming we _only_ want the count here, we can replace the previous last\r\n% 2 lines of code with a call to |count| and more efficiently attain our\r\n% goal.  The great thing about the workflow of building up the complex\r\n% pattern is the versatility it affords you.\r\n\r\nhmw = count(codeFile,vowelWords2Pat,\"IgnoreCase\", true)\r\n\r\n%% \r\n% \r\n\r\n\r\n%%\r\n% Or I could replace |lettersPattern(0,Inf)| with\r\n% |optionalPattern(lettersPattern)|. Being able to give patterns to\r\n% functions like |count|, |startsWith| and |contains| is the biggest win.\r\n%%\r\n% _Best Practice_\r\n%\r\n% We have found that it is best to build up a pattern by joining smaller\r\n% pieces.  It makes it easier to understand what you are doing, where you\r\n% are or are not applying case sensitivity, etc.\r\n\r\n\r\n%% Example 3: Converse - Find Words Beginning with Consonants\r\n% Suppose instead we want words starting with consonants.  Here's the\r\n% |regexp| way.\r\nconsRegexp = regexpi(codeFile, '\\<(?![aeiou])[a-z]+', 'match');\r\n%%\r\n% And using a pattern\r\nconsPat = extract(codeFile, ...\r\n    alphanumericBoundary + ...\r\n    ~lookAheadBoundary(caseInsensitivePattern(characterListPattern('aeiou')))...\r\n    + lettersPattern);\r\n%%\r\n% And finally using neither |regexp| nor a consonant pattern.  Instead, use\r\n% the negation of the starting with vowel words.\r\n% This is the easiest to understand, perhaps.\r\n\r\nconsWords = words(~startsWith(words, caseInsensitivePattern(characterListPattern('aeiou'))));\r\n\r\n%% \r\n% The astute reader will see that the answers here do not agree.  See the\r\n% Appendix at the end for details and how to get the answers to align.\r\n\r\n%% Example 4: Looking for Files with Certain Extensions\r\n% We audited all of the regular expressions used in one stage in our test\r\n% system, and found that around 50% of them could be replaced by |endsWith|\r\n% with NO PATTERN at all. Previously we used |regexp| but that is a huge\r\n% hammer for the job. I think looking for files with a particular file\r\n% extension may have been a common use case. like,\r\n% \r\n%                 regexp(fileName, '.txt$')\r\n%\r\n% which has two bugs!\r\n% You need |isempty|, and |'once'|:\r\n%\r\n%                 ~isempty(regexp(fileName, '.txt$', 'once'))\r\n%\r\n% And you also have to escape the dot, which everyone forgets to do.\r\n%\r\n%                 ~isempty(regexp(fileName, '\\.txt$', 'once'))\r\n%\r\n% Instead now you simply do\r\n%\r\n%                 endsWith(fileName, '.txt')\r\n%\r\n% The interesting things is that this uses no pattern at all but uses a\r\n% function, |endsWith|, that could take a pattern. \r\n%  \r\n% Suppose you now want to check for 2 different extensions. Easily done.\r\n% |endsWith| supports multiple search strings, and treats them as an |or|.\r\n% This is faster but a bit more limited than doing a search with a proper\r\n% pattern.\r\n%\r\n%                 endsWith(fileName, [\".txt\", \".somethingElse\"])\r\n%\r\n% pattern with explicit or\r\n%\r\n%                 endsWith(fileName, \".txt\" | \".somethingElse\")\r\n%\r\n%% What about a File with No Extension\r\n% What if a filename ends with either txt or no extension at all?\r\n%\r\n%                 endsWith(fileName, '.txt') || ~contains(fileName, '.')\r\n%\r\n% This is for a single file, without a full pathname.\r\n\r\n%% How's Your Search Going?\r\n% Are you able to make good use of patterns in MATLAB and are able (or not)\r\n% to eliminate some or all uses of |regexp|.  Let us know\r\n% <https:\/\/blogs.mathworks.com\/loren\/?p=3882#respond here>.\r\n\r\n%% Appendix\r\n% As promised, I will describe here why the consonant answers do not agree\r\n% and how to make them the same.\r\n%\r\n% The |words| variable has groups of consecutive letters.  And we had some\r\n% names in the |num2str| code using numbers as well, e.g., |mat2str|.  This\r\n% translated into 2 words, |mat| and |str|.  We can fix this using\r\n%\r\n%                 words = extract(codeFile, alphanumericBoundary ...\r\n%                         + lettersPattern + alphanumericBoundary);\r\n%\r\n% This means the |regexp| version is:\r\n%\r\n%                 consRegexp = regexpi(codeFile, '\\<(?![aeiou])[a-z]+\\>', 'match');\r\n%\r\n% and the corresponding pattern:\r\n%\r\n%                 consPat = extract(codeFile, ...\r\n%                   alphanumericBoundary + ...\r\n%                   ~lookAheadBoundary(caseInsensitivePattern(characterListPattern('aeiou')))...\r\n%                   + lettersPattern \u2026\r\n%                   + alphanumericBoundary);\r\n%\r\n% Phew! That's a mouthful!  But pretty readable too.\r\n\r\n\r\n\r\n##### SOURCE END ##### a0408f98768044ccb3cf70c5a05b0399\r\n-->","protected":false},"excerpt":{"rendered":"<!--introduction--><p>Do you get clammy hands when you have to search for a string pattern, not just a particular string?  Does the thought of struggling with <tt><a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/regexp.html\">regexp<\/a><\/tt> make you sweat?... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/loren\/2020\/12\/03\/stressed-when-searching-for-strings\/\">read more >><\/a><\/p>","protected":false},"author":39,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[6,2],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/3882"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/users\/39"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/comments?post=3882"}],"version-history":[{"count":3,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/3882\/revisions"}],"predecessor-version":[{"id":3968,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/posts\/3882\/revisions\/3968"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/media?parent=3882"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/categories?post=3882"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/loren\/wp-json\/wp\/v2\/tags?post=3882"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}