How to get longest substring from array of strings using javascript - javascript

I have array:
let arr = ["logerror", "log:today", "log:1"]
I am looking for function how to get longest substring from this items.
Result:
log
Another example:
let arr = ["dog+ěě+", "dog15qwqqq", "dogggggg"]
Result:
dog
Sure, I can write some algorithm, but is there any simple way?
How? Thanks

If you can phrase your question succinctly, you can often find what to search for. In this case, it looks like:
"Find the longest common substring from within an array of strings"
A quick google reveals an algorithm for finding the largest common substring between two strings:
https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Longest_common_substring
I don't want to copy the code as written there, as unsure of the copyright, but you could take the implementation and take something that will work with your array.
I would note that for large arrays, this may turn out to be a lengthy operation...

I used a simple approach:
It sorts the array using sort() method.
Then, the most important step is to look just at the first and last items.
function commonSubsequence(array){
let sortedArray = array.sort();
let first = sortedArray[0];
let last = sortedArray.pop();
let length = first.length;
let index = 0;
while(index<length && first[index] === last[index])
index++;
return first.substring(0, index);
}
console.log(commonSubsequence(["logerror", "log:today", "log:1"]));
console.log(commonSubsequence(["dog+ěě+", "dog15qwqqq", "dogggggg"]));

Here is my suggestion
function subStrArr(arr) {
let chars = arr[0].split(""), sub = "";
for (let i=0;i<chars.length;i++) {
for (let j=1;j<arr.length;j++) {
if (arr[j].indexOf(chars[i])==-1) return sub;
}
sub+=chars[i];
}
}
let arr1 = ["logerror", "log:today", "log:1"];
let arr2 = ["dog+ěě+", "dog15qwqqq", "dogggggg"];
console.log(subStrArr(arr1))
console.log(subStrArr(arr2))

After some looking around I went for the string-algorithms npm package, which did the job nicely for me.
From the docs:
import { longestCommonSubstring } from 'string-algorithms';
const strings = [
'12apple',
'3apple4',
'apple56'
];
console.log(longestCommonSubstring(strings));
produces the output apple.

without DP approach
var lcs = function (n, m) {
let lcs = 0 //to store longest common substring
let s1 = n.length
let s2 = m.length
for(let i = 0;i < s1;i++){
for(let j = 0; j< s2;j++){
let track = 0
//if letter are same, do while to check next letter
if(n[i] == m[j]){
while(i + track < s1 && j + track < s2 && n[i + track] == m[j + track]){
track += 1 // to track
if (lcs < track) {
lcs += 1
}
}
}
}
}
return lcs;
};
var m = "abcdxyz"
var n = "xyzabcd" // 4
// var m = "dadef"
// var n = "adwce"//2
// var m = "acdghr";
// var n = "bgh"; //2
// var m = "A"
// var n = "A" //1
console.log(lcs(m, n));

Related

Insert data to array of array

i have this loop.
now, in a[i] im putting name of college, for example UCLA.
in temp i have the name of the player i want to insert into the a[i][p]
When im looking at temp im seeing the name that i actualy wants to insert, but them im
doing this line a[i][p] = temp;
im seeing in a[0][0]='U',
why?
var a = [[]];
var p = 0;
var temp;
for (var i = 0; i < uniq.length; i++) {
a[i] = uniq[i];
for (var k = 0; k < data.players.length; k++) {
if (uniq[i] == data.players[k].college)
{
temp = data.players[k].name
a[i][p] = temp;
p++;
}
}
p = 0;
}
console.log(a[0][0])
The answer is:
first you put UCLA as the value of a[0]
now your array looks like this:
console.log(a[0]) // expected "UCLA"
strings in JavaScript can be iterated over - this means (roughly), that you can get a[0][0] in this case: the first item of the first item of the a array - and that is "the first character of the string at the first index in a"
console.log(a[0][0]) // expected: "U", if a[0] is "UCLA"
const a = []
a[0] = "UCLA"
console.log(a)
console.log(a[0])
console.log(a[0][0])
You need to do it a bit differently (this could be one approach, but there could be more):
const a = {} // this is an object, not an array!
a["UCLA"] = "name of tthe player"
console.log(a)
console.log(a["UCLA"])
Or, if you need an array, then you could do:
const a = []
a[0] = { uni: "UCLA", player: [] }
a[0].player[0] = "name of the player"
console.log(a)
when indexing JavaScript strings they behave like arrays and you actually get a 1 letter string with the character on the index you requested
for example:
var dummy = "house";
console.log(dummy[1]);
//this is going to return o (character in index 1 of house)
note that when indexing a string you are going to get another string and not a char like in other languages,
to achieve what you are trying to do you can use a dictionary like this:
var schools = {};
for (var i = 0; i < uniq.length; i++) {
schools[uniq[i]] = [];
for (var k = 0; k < data.players.length; k++) {
if (uniq[i] == data.players[k].college)
{
schools[uniq[i]].push(data.players[k].name);
}
}
}
at the end of this you can access schools either by indexing, by string key (college name) or with a simple foreach
schools[1]; //this is gonna give ["firstname","secondname","etc"] (player names of the second school)
schools["UCLA"]; //this is gonna give ["firstname","secondname","etc"] (player names of UCLA school)

Find indices within a string where any combination of an array of words is found

Sample data:
String: "barfoofoobarthefoobarman"
Array of words: ["bar", "foo", "the"]
Output:
[6, 9, 12]
I was asked this question during an interview. Due to time constraint, I tried to find all the possible words that could be made out of the array of words (i. e. "barfoothe"), but was told that would not scale for large arrays. Was suggested to use a map data structure, but I think my solution doesn't scale either, and it's brute forced.
Here's the solution.
var solution = function(string, words) {
let output = [];
let wordsMap = new Map();
let wordsNumber = words.length;
let wordLength = words[0].length;
words.forEach((word) => {
if (!wordsMap.has(word))
wordsMap.set(word, 1);
else
wordsMap.set(word, wordsMap.get(word) + 1);
});
for (let i = 0; i <= string.length-(wordsNumber*wordLength); i+=wordLength) {
let tempMap = new Map(wordsMap);
let check = true;
let tempString = string.substring(i, i + wordsNumber*wordLength);
for (let j = 0; j <= tempString.length - wordLength; j += wordLength) {
let tempString2 = tempString.substring(j, j + wordLength);
if (tempMap.has(tempString2))
tempMap.set(tempString2, tempMap.get(tempString2) - 1);
}
for (let val of tempMap.values()){
if (val !== 0){
check = false
break;
}
}
if (check)
output.push(i)
}
console.log(output);
}
solution("barfoothefoobarman", ["foo", "bar"]);
Any suggestion for a smarter solution?
You could create a dynamic regular expression.
const words = ['foo', 'bar']
const rx = new RegExp(words.join('|'), 'g')
// todo escape special characters
Then search away.
const counts = words.map(it=>0) // [0,0]
// todo use map or object to track counts instead of array
while (m = rx.exec(inputString)) {
const index = words.indexOf(m[0])
counts[index]++
}
Thank you for your question. I think the question in the interview was less about the right solution and more about the right approach.
The trickiest part is actually just finding the word combinations. There are several approaches here. For me it's a clear case for recursion.
So my approach would be:
find all word combinations, except combinations with itself (for example: foofoo or barbar).
iterate through the word combinations and ask whether they are contained in the string.
extra: Sort SolutionArray
Done!
Note: I use indexOf() for point 2 but I think a regex match would make it even better because you find all possibilities of a word in a string and not just the first one like with indexOf. Would make sense for longer strings.
const arr = ["foo", "bar"];
const str = "barfoothefoobarman"
let res = [];
const combinations = (len, val, existing) => {
if (len == 0) {
res.push(val);
return;
}
for(let i=0; i<arr.length; i++) {
if(! existing[i]) {
existing[i] = true;
combinations(len-1, val + arr[i], existing);
existing[i] = false;
}
}
}
const buildCombinations = (arr = []) => {
for(let i = 0; i < arr.length; i++) {
combinations(arr.length - i, "", []);
}
};
buildCombinations(arr);
// exclude the base wordes from result array
newRes = res.filter((e) => {
if (! arr.includes(e)) {
return e;
}
})
console.log('all word combinations:', newRes);
// get the string position
const _positions = [];
newRes.forEach((w) => {
let res = str.indexOf(w);
if (res != -1 && ! _positions.includes(res)) {
_positions.push(res);
}
})
// sort array and use Float64Array to speed up
const positions = new Float64Array(_positions)
console.log('positions', positions.sort())

matching items from two list in nodejs

my current problem is I have two mongodb collections. My code in essence takes each name from table 1 which is 250000~ items then it compares against the list of names from table 2 (3000~) to try and match.
now I'm just brute forcing this by going through every item in the 250000 list and looking at every item in the 3000 list to see if it matches
the problem is that this takes about 2 hours to run through
Is there a faster way to do matching? I've put the stripped down version of my code below so you can see it's a simple loop in a loop.
Thanks
const SCCM = await ProgramDev.find({})
const Cat = await Software.find({})
for (var i = 0, len = SCCM.length; i < len; i++) {
for (var n = 0, Catlen = Cat.length; n < Catlen; n++) {
var program = SCCM[i]['program name']
var software = Cat[n]['Application']
var sat = Cat[n].Status
if (SCCM[i].Status == undefined) {
if (program == software) {
SCCM[i].Status = sat
break
}
}
}
if (SCCM[i].Status == undefined) {
SCCM[i].Status = "Un-authorised"
SCCM[i].matchType = "None"
}
}
If you have already extracted each name from collection1 and from collection2 into arrays this should work faster
array1 = ['john','smith','vova','putin', 'eug', 'hilly', 'tom', 'smith','vova','putin' ]
array2 = ['vova', 'dr.cacis']
const newArray = array1.filter(value => array2.includes(value));
console.log(newArray);
Output matches
[ 'vova', 'vova' ]
You can also use Array.prototype.includes
or Array.prototupe.indexOf
You can try to use map if you don't have memory restriction and put the values of Cat in the map like:
var catMap = new Map();
for (var n = 0, Catlen = Cat.length; n < Catlen; n++) {
catMap.set(Cat[n]['Application'] , Cat[n].Status);
}
And run the second loop on SCCM like:
for (var i = 0, len = SCCM.length; i < len; i++) {
if (SCCM[i].Status == undefined && catMap.has(SCCM[i]['program name'])){
SCCM[i].Status = catMap.get(SCCM[i]['program name']);
}else if(SCCM[i].Status == undefined){
SCCM[i].Status = "Un-authorised"
SCCM[i].matchType = "None"
}
}
So, the first loop will take O(n) and second loop will take O(m), total will be O(m+n) time complexity.
The space complexity will be O(n) because of map.
Overall, program will take less time to run.

Cut JS string present in an array at every occurrance of a particular substring and append to same array

Note:
At this point in time, I'm unable to word the question title better. If someone is able to put it accross better, please go right ahead!
What I have:
var array = ["authentication.$.order", "difference.$.user.$.otherinformation", ... , ...]
What I need:
["authentication", "authentication.$", "authentication.$.order",
"difference", "difference.$", "difference.$.user", "difference.$.user.$",
"difference.$.user.$.otherinformation"]
Basically, wherevever I see .$., I need to preserve it, then append everything before the occourrence of .$. along with everything before the occourrence of .$
Example:
difference.$.user.$.otherinformation should be parsed to contain:
difference
difference.$
difference.$.user
difference.$.user.$
difference.$.user.$.otherinformation
I'm strongly feeling that some sort of recursion is to be involved here, but have not progressed in that direction yet.
Below is my implementation for the same, but unfortunately, my when my substring matches the first occourrence of .$., it stops and does not proceed to further check for other occurrences of .$. in the same string.
How best can I take this to closure?
Current flawed implementation:
for(var i=0; i<array.length; i++){
// next, replace all array field references with $ as that is what autoform's pick() requires
// /\.\d+\./g,".$." ==> replace globally .[number]. with .$.
array[i] = array[i].replace(/\.\d+\./g,".$.");
if(array[i].substring(0, array[i].lastIndexOf('.$.'))){
console.log("Substring without .$. " + array[i].substring(0, array[i].indexOf('.$.')));
console.log("Substring with .$ " + array[i].substring(0, array[i].indexOf('.$.')).concat(".$"));
array.push(array[i].substring(0, array[i].indexOf('.$.')).concat(".$"));
array.push(array[i].substring(0, array[i].indexOf('.$.')));
}
}
// finally remove any duplicates if any
array = _.uniq(array);
A functional single liner could be;
var array = ["authentication.$.order", "difference.$.user.$.otherinformation"],
result = array.reduce((r,s) => r.concat(s.split(".").reduce((p,c,i) => p.concat(i ? p[p.length-1] + "." + c : c), [])), []);
console.log(result);
You can use this function inside your array loop.
var test = "difference.$.user.$.otherinformation";
function toArray(testString) {
var testArr = testString.split(".")
var tempString = "";
var finalArray = [];
for (var i = 0; i < testArr.length; i++) {
var toTest = testArr[i];
if (toTest == "$") {
tempString += ".$"
} else {
if (i != 0) {
tempString += ".";
}
tempString += toTest;
}
finalArray.push(tempString)
}
return finalArray;
}
console.log(toArray(test))
I used a Regex expression to grab everything until the last occurrence of .$ and the chopped it, until there was nothing left. Reverse at the end.
let results = [];
let found = true;
const regex = /^(.*)\.\$/g;
let str = `difference.\$.user.\$.otherinformation`;
let m;
results.push(str);
while(found) {
found = false;
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
if(m.length > 0) {
found = true;
results.push(m[0]);
str = m[1];
}
}
}
results.push(str);
results = results.reverse();
// Concat this onto another array and keep concatenating for the other strings
console.log(results);
You will just need to loop this over your array, store the results in a temp array and keep concatenating them onto a final array.
https://jsfiddle.net/9pa3hr46/
You can use reduce as follows:
const dat = ["authentication.$.order", "difference.$.user.$.otherinformation"];
const ret = dat.reduce((acc, val) => {
const props = val.split('.');
let concat = '';
return acc.concat(props.reduce((acc1, prop) => {
concat+= (concat ? '.'+ prop : prop);
acc1.push(concat);
return acc1;
}, []));
}, [])
console.log(ret);
Actually recursion is unnecessary for this problem. You can use regular loop with subloop instead.
All you need is:
split each occurence in the array into substrings;
build a series of accumulated values from these substrings;
replace the current element of the array with this series.
Moreover, in order to make replacement to work properly you have to iterate the array in reverse order. BTW in this case you don't need to remove duplicates in the array.
So the code should look like this:
var array = ["authentication.$.order", "difference.$.user.$.otherinformation"];
var SEP = '.$.';
for (var i = array.length-1; i >= 0; i--){
var v = array[i];
var subs = v.replace(/\.\d+\./g, SEP).split(SEP)
if (subs.length <= 1) continue;
var acc = subs[0], elems = [acc];
for (var n = subs.length-1, j = 0; j < n; j++) {
elems[j * 2 + 1] = (acc += SEP);
elems[j * 2 + 2] = (acc += subs[j]);
}
array.splice.apply(array, [i, 1].concat(elems));
}
console.log(array);
Use a simple for loop like below:
var str = "difference.$.user.$.otherinformation";
var sub, initial = "";
var start = 0;
var pos = str.indexOf('.');
for (; pos != -1; pos = str.indexOf('.', pos + 1)) {
sub = str.substring(start, pos);
console.log(initial + sub);
initial += sub;
start = pos;
}
console.log(str);

add elements of an array javascript

Ok, this might be easy for some genius out there but I'm struggling...
This is for a project I'm working on with a slider, I want an array the slider can use for snap points/increments... I'm probably going about this in a mental way but its all good practice! Please help.
var frootVals = [1,2,3,4,5];
var frootInc = [];
for (i=0; i<=frootVals.length; i++) {
if (i == 0){
frootInc.push(frootVals[i]);
}
else{
frootInc.push(frootInc[i-1] += frootVals[i])
}
};
What I'm trying to do is create the new array so that its values are totals of the array elements in frootVals.
The result I'm looking for would be this:
fruitInc = [1,3,6,10,15]
For a different take, I like the functional approach:
var frootVals = [1,2,3,4,5];
var frootInc = [];
var acc = 0;
frootVals.forEach(function(i) {
acc = acc + i;
frootInc.push(acc);
});
var frootVals = [1,2,3,4,5]
, frootInc = [];
// while i < length, <= will give us NaN for last iteration
for ( i = 0; i < frootVals.length; i++) {
if (i == 0) {
frootInc.push(frootVals[i]);
} else {
// rather than frootIne[ i-1 ] += ,
// we will just add both integers and push the value
frootInc.push( frootInc[ i-1 ] + frootVals[ i ] )
}
};
There were a few things wrong with your code check out the commenting in my code example. Hope it helps,
This will do:
var frootVals = [1,2,3,4,5];
var frootInc = [];
for (i=0; i < frootVals.length; i++) { // inferior to the length of the array to avoid iterating 6 times
if (i == 0) {
frootInc.push(frootVals[i]);
}
else {
frootInc.push(frootInc[i-1] + frootVals[i]) // we add the value, we don't reassign values
}
};
alert(JSON.stringify(frootInc));
jsfiddle here: http://jsfiddle.net/f01yceo4/
change your code to:
var frootVals = [1,2,3,4,5];
var frootInc = [frootvals[0]]; //array with first item of 'frootVals' array
for (i=1; i<frootVals.length; i++) {
frootInc.push(frootInc[i-1] + frootVals[i]); //remove '='
}
Here's a very simple pure functional approach (no vars, side-effects, or closures needed):
[1,2,3,4,5].map(function(a){return this[0]+=a;}, [0]);
// == [1, 3, 6, 10, 15]
if you name and un-sandwich the function, you can use it over and over again, unlike a hard-coded var name, property name, or for-loop...

Categories

Resources