How to get mapped values from string? - javascript

I have a string like,
const string =
"DEVICE_SIZE IN ('036','048','060','070') AND DEVICE_VOLTAGE IN ('1','3') AND NOT DEVICE_DISCHARGE_AIR IN ('S') AND NOT DEVICE_REFRIGERANT_CIRCUIT IN ('H','C')";
From this, I need to map the respective key and value like, DEVICE_SIZE: ["036", "048", "060", "070"]
Current Result:
const string =
"DEVICE_SIZE IN ('036','048','060','070') AND DEVICE_VOLTAGE IN ('1','3') AND NOT DEVICE_DISCHARGE_AIR IN ('S') AND NOT DEVICE_REFRIGERANT_CIRCUIT IN ('H','C')";
const res = string.split('IN ');
const regExp = /\(([^)]+)\)/;
const Y = 'AND';
const data = res.map((item) => {
if (regExp.exec(item)) {
return {
[item.slice(item.indexOf(Y) + Y.length)]: regExp.exec(item)[1],
};
}
});
console.log('data ', data);
Expected Result:
[
{ "DEVICE_SIZE": ["036", "048", "060", "070"] },
{ "DEVICE_VOLTAGE": ["1", "3"] },
{ "NOT DEVICE_DISCHARGE_AIR": ["s"] },
{ "NOT DEVICE_REFRIGERANT_CIRCUIT": ["H", "C"] },
];
I couldn't get the exact result based on my try in the current result. Could you please kindly help me to achieve the above given expected result?
Note: I am trying the above to achieve the end result mentioned in my previous question
How to get valid object from the string matching respective array?

You could make use of a single regex with 2 capture groups to match the key and the value.
((?:\bNOT\s+)?\w+)\s+IN\s+\('([^()]*)'\)
See the regex demo.
The pattern matches:
( Capture group 1
(?:\bNOT\s+)? Optionally match the word NOT followed by 1+ whitespace chars
\w+ Match 1 or more word characters
) Close group
\s+IN\s+ Match the word IN between whitespace characters
\(' Match ('
([^()]*) Capture group 2, match 1+ occurrences of any char except ( and )
'\) Match ')
To create the dynamic keys for the object, you can make use of Object Initializer and you can split on ',' to create the resulting array for the value.
const regex = /((?:\bNOT\s+)?\w+)\s+IN\s+\('([^()]*)'\)/g;
const string = "DEVICE_SIZE IN ('036','048','060','070') AND DEVICE_VOLTAGE IN ('1','3') AND NOT DEVICE_DISCHARGE_AIR IN ('S') AND NOT DEVICE_REFRIGERANT_CIRCUIT IN ('H','C')";
const data = Array.from(
string.matchAll(regex), m =>
({
[m[1]]: m[2].split("','")
})
);
console.log(data);

You could have split by AND first and then split again with IN to separate the key and the value part.
This would also work:
const string =
"DEVICE_SIZE IN ('036','048','060','070') AND DEVICE_VOLTAGE IN ('1','3') AND NOT DEVICE_DISCHARGE_AIR IN ('S') AND NOT DEVICE_REFRIGERANT_CIRCUIT IN ('H','C')";
const output = string
.split("AND")
.map((item) => item.split("IN").map((text) => text.trim()))
.map(([key, value]) => ({
[key]: value.replace(/[\(\)\']/g, "").split(","),
}));
console.log(output);

You can use Object.fromEntries, which transforms key-value tuples [string, any] into an object with those tuples:
const data = Object.fromEntries(res.map((item) => {
if (regExp.exec(item)) {
return [item.slice(item.indexOf(Y) + Y.length).trim(), regExp.exec(item)[1].split(',').map(value => value.slice(1, -1))];
}
return undefined;
}).filter(Boolean));
and then continue from there.

Related

Is there a way to remove a newline character within a string in an array?

I am trying to parse an array using Javascript given a string that's hyphenated.
- foo
- bar
I have gotten very close to figuring it out. I have trimmed it down to where I get the two items using this code.
const chunks = input.split(/\ ?\-\ ?/);
chunks = chunks.slice(1);
This would trim the previous input down to this.
["foo\n", "bar"]
I've tried many solutions to get the newline character out of the string regardless of the number of items in the array, but nothing worked out. It would be greatly appreciated if someone could help me solve this issue.
You could for example split, remove all the empty entries, and then trim each item to also remove all the leading and trailing whitespace characters including the newlines.
Note that you don't have to escape the space and the hyphen.
const input = `- foo
- bar`;
const chunks = input.split(/ ?- ?/)
.filter(Boolean)
.map(s => s.trim());
console.log(chunks);
Or the same approach removing only the newlines:
const input = `- foo
- bar`;
const chunks = input.split(/ ?- ?/)
.filter(Boolean)
.map(s => s.replace(/\r?\n|\r/g, ''));
console.log(chunks);
Instead of split, you might also use a match with a capture group:
^ ?- ?(.*)
The pattern matches:
^ Start of string
?- ? Match - between optional spaces
(.*) Capture group 1, match the rest of the line
const input = `- foo
- bar`;
const chunks = Array.from(input.matchAll(/^ ?- ?(.*)/gm), m => m[1]);
console.log(chunks);
You could loop over like so and remove the newline chars.
const data = ["foo\n", "bar"]
const res = data.map(str => str.replaceAll('\n', ''))
console.log(res)
Instead of trimming after the split. Split wisely and then map to replace unwanted string. No need to loop multiple times.
const str = ` - foo
- bar`;
let chunks = str.split("\n").map(s => s.replace(/^\W+/, ""));
console.log(chunks)
let chunks2 = str.split("\n").map(s => s.split(" ")[2]);
console.log(chunks2)
You could use regex match with:
Match prefix "- " but exclude from capture (?<=- ) and any number of character different of "\n" [^\n]*.
const str = `
- foo
- bar
`
console.log(str.match(/(?<=- )[^\n]*/g))
chunks.map((data) => {
data = data.replace(/(\r\n|\n|\r|\\n|\\r)/gm, "");
return data;
})
const str = ` - foo
- bar`;
const result = str.replace(/([\r\n|\n|\r])/gm, "")
console.log(result)
That should remove all kinds of line break in a string and after that you can perform other actions to get the expected result like.
const str = ` - foo
- bar`;
const result = str.replace(/([\r\n|\n|\r|^\s+])/gm, "")
console.log(result)
const actualResult = result.split('-')
actualResult.splice(0,1)
console.log(actualResult)

array of invalid chars - checking the start of string to validate it does not start with invalid chars

I have an array of invalid strings,
let invalidStrings = ["070", "0870", "030"]
I want to check a phone number/string does not start with anything in theinvalidStrings array, I have tried,
if (substrings.some(v => str !== v)) {
// Will only return when the `str` is not included in the `substrings`
}
but that checks the entire string for any appearance of an invalidString?
You can filter an array that contains the invalid string by using filter().
let invalidStrings = ["070", "0870", "030"]
let substrings = ["070100", "1107011", "1230870", "030555030", "123456789"]
let filteredStrings = substrings.filter(v => { // filter the array you want to work on
return invalidStrings.find(iStr => v.startsWith(iStr)) //check if each string contains one of the invalid strings
})
console.log(filteredStrings);
Output
Array [ "070100", "030555030" ]

How to match two non-consecutive words in a String for React/Javascript search?

I have a search bar which relies on this filter method.
I concatenate all the search strings in a variable concat, and then use either .includes() or .match() as shown below. If searched for multiple words, this only returns a result if the words occur consecutively in concat.
However, I want it to match ANY two words in concat, not just consecutive ones. Is there a way to do this easily?
.filter((frontMatter) => {
var concat =
frontMatter.summary +
frontMatter.title +
frontMatter.abc+
frontMatter.def+
frontMatter.ghi+
frontMatter.jkl;
return concat.toLowerCase().match(searchValue.toLowerCase());
});
Also tried;
.filter((frontMatter) => {
const concat =
frontMatter.summary +
frontMatter.title +
frontMatter.abc+
frontMatter.def+
frontMatter.ghi+
frontMatter.jkl;
return concat.toLowerCase().includes(searchValue.toLowerCase());
});
Thanks!
Everything is explained in the comments of the code.
If you don't care that "deter" matches the word "undetermined"
.filter((frontMatter) => {
// Get the front matter into a string, separated by spaces
const concat = Object.values(frontMatter).join(" ").toLowerCase();
// Look for a string in quotes, if not then just find a word
const regex = /\"([\w\s\\\-]+)\"|([\w\\\-]+)/g;
// Get all the queries
const queries = [...searchValue.toLowerCase().matchAll(regex)].map((arr) => arr[1] || arr[2]);
// Make sure that every query is satisfied
return queries.every((q) => concat.includes(q));
});
If you DO care that "deter" should NOT match the word "undetermined"
.filter((frontMatter) => {
// Get the front matter into a string, separated by spaces
// The prepended and appended spaces are important for the regex later!
const concat = ` ${Object.values(frontMatter).join(" ").toLowerCase()} `;
// Look for a string in quotes, if not then just find a word
const regex = /\"([\w\s\\\-]+)\"|([\w\\\-]+)/g;
// Get all the queries
const queries = [...searchValue.toLowerCase().matchAll(regex)].map((arr) => arr[1] || arr[2]);
// Make sure that every query is satisfied
// [\\s\\.?!_] and [\\s\\.?!_] check for a space or punctuation at the beginning and end of a word
// so that something like "deter" isn't matching inside of "undetermined"
return queries.every((q) => new RegExp(`[\\s\\.?!_]${q}[\\s\\.?!_]`).test(concat));
});
I'd use .reduce to count up the number of matches, and return true if there are at least 2:
const props = ['summary', 'title', 'abc', 'def', 'ghi', 'jkl'];
// ...
.filter((frontMatter) => {
const lowerSearch = searchValue.toLowerCase();
const matchCount = props.reduce(
(a, prop) => a + lowerSearch.includes(frontMatter[prop].toLowerCase()),
0
);
return matchCount >= 2;
})

Could a regular expression be used to find text between pairs of delimiters

I need to parse an email template for custom variables that occur between pairs of dollar signs, e.g:
$foo$bar$baz$foo$bar$baz$wtf
So I would want to start by extracting 'foo' above, since it comes between the first pair (1st and 2nd) of dollar signs. And then skip 'bar' but extract 'baz' as it comes between the next pair (3rd and 4th) of dollar signs.
I was able to accomplish this with split and filter as below, but am wondering, if there's a way to accomplish the same with a regular expression instead? I presume some sort of formal parser, recursive or otherwise, could be used, but that would seem like overkill in my opinion
const body = "$foo$bar$baz$foo$bar$baz$wtf";
let delimitedSegments = body.split('$');
if (delimitedSegments.length % 2 === 0) {
// discard last segment when length is even since it won't be followed by the delimiter
delimitedSegments.pop();
}
const alternatingDelimitedValues = delimitedSegments.filter((segment, index) => {
return index % 2;
});
console.log(alternatingDelimitedValues);
OUTPUT: [ 'foo', 'baz', 'bar' ]
Code also at: https://repl.it/#dexygen/findTextBetweenDollarSignDelimiterPairs
Just match the delimiter twice in the regexp
const body = "$foo$bar$baz$foo$bar$baz$wtf";
const result = body.match(/\$[^$]*\$/g).map(s => s.replace(/\$/g, ''));
console.log(result);
You could use this regex /\$\w+\$/g to get the expected output'
let regex = /\$\w+\$/g;
let str = '$foo$bar$baz$foo$bar$baz$wtf';
let result = str.match(regex).map( item => item.replace(/\$/g, ''));
console.log(result);
You can use capturing group in the regex.
const str1 = '$foo$bar$baz$foo$bar$baz$wtf';
const regex1 = /\$(\w+)\$/g;
const str2 = '*foo*bar*baz*foo*bar*baz*wtf';
const regex2 = /\*(\w+)\*/g;
const find = (str, regex) =>
new Array(str.match(regex).length)
.fill(null)
.map(m => regex.exec(str)[1]);
console.log('delimiters($)', JSON.stringify(find(str1, regex1)));
console.log('delimiters(*)', JSON.stringify(find(str2, regex2)));

Typescript parse string into groups of digits as 345-67 and text with both words and digits

My string is in format "[111-11] text here with digits 111, [222-22-22]; 333-33 text here" and I want to parse so that I have the code [111-11], [222-22-22], [333-33] and its corresponding text.
I don't have fixed splitter except for the code xxx-xx or xxx-xx-xx.
I tried in this way but it fails to get digits at desc part. \D will get anything but digits.
let text = "[111-11] text here with digits 111, [222-22-22]; 333-33 text here";
let codes=[];
let result = text.replace(/(\d{3}(-\d{2})+)(\D*)/g,(str, code, c, desc) => {
desc = desc.trim().replace(/[\[\]']+/g,'');
if (code) codes.push({'code':code.trim(),'desc': desc});
return str;
}); //parse and split codes
Finally, I want result in this style:
[{code:'111-11', desc:'text here with digits 111'},
{code:'222-22-22', desc:''},
{code:'333-33', desc:'text here'}]
I really appreciate the help.
You could take a search for the brackets values and the following text in groups and a positive lookahead for the bracket part or end of string. Then destructuring the string and push the wanted object.
const regex = /\[?(\d{3}(-\d\d)+)\]?(.*?)(?=\[?\d{3}(-\d\d)+\]?|$)/gm;
const str = `[111-11] text here with digits 111, [222-22-22]; 333-33 text here`;
var m,
code, desc,
result= [];
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
({ 1: code, 3: desc } = m);
result.push({ code, desc })
}
console.log(result);
Another approach:
const Sep = ','
const PatSep = /[,;]/g
// skippng first spaces, then getting the numbers (and ignoring the brackets
// if presents), then gets rest
const PatPart = /^\s*\[?(\d{3}(-\d{2})+)]?(.*)$/
const src =
"[111-11] text here with digits 111, [222-22-22]; 333-33 text here"
const parse = src => {
// to avoir differents terminations
const normalized = src.replace (PatSep, Sep)
return normalized.split (Sep).reduce((acc, part) => {
// getting code and desc from part
const [_, code, __, desc] = part.match (PatPart)
// accumulating in desired array
return [
...acc,
{code, desc}
]
}, [])
}
console.log(parse (src))
;)

Categories

Resources