Sorting array by multiple prefixes - javascript

I have an array with values. I'm sorting it with a condition to keep certain items at the top. That is working so far. Now I want to run two conditions for example I've two prefixes to be matched with every item in the array: tableprefix and secondaryprefix. What I have already achieved is keeping tableprefix at the top. And the rest of the items must be sorted alphabetically.
What I want to achieve:
1: Array items matching tableprefix at the very top // already achieved
2: Array items matching secondaryprefix comes after tableprefix // can't figure out
3: Sort rest of the items alphabetically // already achieved
Array:
columns = [
"es_officer_name",
"es_officer_fname",
"es_officer_apply_status",
"es_officer_dob",
"es_wl_1_11_test_id",
"es_officer_id",
"es_designation_id",
"es_wl_1_11_test_edit_date",
"es_designation_title",
"es_employment_type_id",
"es_employment_type_name",
"es_service_type_id",
"es_service_type_name",
"es_wl_1_11_test_added_date",
"es_bps_id",
"es_bps_title",
"es_department_id",
"es_department_name"
];
Prefix:
var tablePrefix = "es_wl";
Sorting Algo:
columns.sort(function(a, b)
{
if (a.indexOf(tablePrefix))
{
if (b.indexOf(tablePrefix))
{
return a.localeCompare(b);
}
else
{
return 1;
}
}
else
{
if (b.indexOf(tablePrefix))
{
return -1;
}
else
{
return 0; // All prefixed are considered equal
}
}
});
Sorted Result:
[
"es_wl_1_11_test_id",
"es_wl_1_11_test_edit_date",
"es_wl_1_11_test_added_date",
"es_bps_id",
"es_bps_title",
"es_department_id",
"es_department_name",
"es_designation_id",
"es_designation_title",
"es_employment_type_id",
"es_employment_type_name",
"es_officer_apply_status",
"es_officer_dob",
"es_officer_fname",
"es_officer_id",
"es_officer_name",
"es_service_type_id",
"es_service_type_name"
]
Now what I want is to keep all the items matching the tablePrefix "es_wl" at the very top. But at the same time Add another prefix secondaryPrefix "es_officer" so that all matching items would come right after "es_wl" items and then the usual alphabetical sorting.
I have looked at several items and applied it but the items are not even moving from their place. I guess my logic is completely wrong for integrating second prefix and keeping the first one and sorting afterwards.

It is done here, run the snippet.
var columns = [
"es_officer_name",
"es_officer_fname",
"es_officer_apply_status",
"es_officer_dob",
"es_wl_1_11_test_id",
"es_officer_id",
"es_designation_id",
"es_wl_1_11_test_edit_date",
"es_designation_title",
"es_employment_type_id",
"es_employment_type_name",
"es_service_type_id",
"es_service_type_name",
"es_wl_1_11_test_added_date",
"es_bps_id",
"es_bps_title",
"es_department_id",
"es_department_name"
];
var tablePrefix = "es_wl";
var secondTablePrefix = "es_officer"
columns.sort((function(firstPattern, SecondPattern) {
this.sorting = function(a, b, tablePrefix1, primarySort) {
if (a.indexOf(tablePrefix1) != -1) {
if (b.indexOf(tablePrefix1) != -1) {
return a.localeCompare(b);
} else {
return -1;
}
} else if (b.indexOf(tablePrefix1) != -1) {
return 1;
} else {
return primarySort ? 0 : a.localeCompare(b); // All prefixed are considered equal
}
}
return function(a, b) {
var result = sorting(a, b, firstPattern, 1);
if (result == 0) {
result = sorting(a, b, SecondPattern, 0);
}
return result;
}
})(tablePrefix, secondTablePrefix));
console.log(columns)
Note: There is an issue in your sorting code - look at the sorting of es_officer it is not alphabetical, I feel even that should be required. It is all done in snippet above.
UPDATE :
To get the id column in top,
var columns = [
"es_officer_name",
"es_officer_fname",
"es_officer_apply_status",
"es_officer_dob",
"es_officer_id",
"es_designation_id",
"es_wl_1_11_test_edit_date",
"es_designation_title",
"es_employment_type_id",
"es_wl_1_11_test_id",
"es_employment_type_name",
"es_service_type_id",
"es_service_type_name",
"es_wl_1_11_test_added_date",
"es_bps_id",
"es_bps_title",
"es_department_id",
"es_department_name"
];
var tablePrefix = "es_wl";
var secondTablePrefix = "es_officer"
columns.sort((function(firstPattern, SecondPattern) {
this.sorting = function(a, b, tablePrefix1, primarySort) {
if (a.indexOf(tablePrefix1) != -1) {
if (b.indexOf(tablePrefix1) != -1) {
return a.indexOf("_id") != -1 ? -1 : b.indexOf("id") != -1 ? 1 : a.localeCompare(b);
} else {
return -1;
}
} else if (b.indexOf(tablePrefix1) != -1) {
return 1;
} else {
return primarySort ? 0 : a.localeCompare(b); // All prefixed are considered equal
}
}
return function(a, b) {
var result = sorting(a, b, firstPattern, 1);
if (result == 0) {
result = sorting(a, b, SecondPattern, 0);
}
return result == "retainIt" ? 0 : result;
}
})(tablePrefix, secondTablePrefix));
console.log(columns)

You could do it with a regular expression. Make the array into a string, search for the words with the prefixes :
var str = columns.toString(); // Array to string
var prefix = "es_wl";
var regex = new RegExp(prefix + "[\\w]+,", "g"); // Word with the prefix
var res = str.match(regex).sort(); // The array with the words that starts with the desired prefix
With multiple prefixes, you can put them in an array. Loop through the array, make multiple regexes and concat() the sorted array together. After that delete the words in the string with the desired prefixes with replace() :
str = str.replace(regex, "");
In total :
columns = [
"es_officer_name",
"es_officer_fname",
"es_officer_apply_status",
"es_officer_dob",
"es_wl_1_11_test_id",
"es_officer_id",
"es_designation_id",
"es_wl_1_11_test_edit_date",
"es_designation_title",
"es_employment_type_id",
"es_employment_type_name",
"es_service_type_id",
"es_service_type_name",
"es_wl_1_11_test_added_date",
"es_bps_id",
"es_bps_title",
"es_department_id",
"es_department_name"
];
var prefix1 = "es_wl";
var prefix2 = "es_officer";
var prefix3 = "es_department";
var prefixes1 = [prefix1, prefix2];
var prefixes2 = [prefix2, prefix1, prefix3];
function makeCustomOrder(prefixes, arr) {
var str = columns.toString();
var res = [];
prefixes.forEach((pre, i) => {
var regex = new RegExp(pre + "[\\w]+,", "g");
res = res.concat(str.match(regex).sort());
str = str.replace(regex, ""); // Delete the words with the desired prefixes
});
res = res.concat(str.split(",").sort()); // Put the sorted rest to the result
return res;
}
var res = makeCustomOrder(prefixes1, columns);
var res2 = makeCustomOrder(prefixes2, columns);
console.log(res); // Example 1
console.log(res2); // Example 2
The benefit of the compact function makeCustomOrder() is that you can use an array with prefixes as much as you want!

Related

How to find the path in a 2d array

I'm trying to work on creating a 2d array
where variable a is true otherwise false.
I know i have to use recursion but i'm not sure how to go about this
var a = [[
[0,0,0,0,0],
[1,1,1,1,0],
[0,0,0,0,0],
[0,1,1,1,1],
[0,0,0,0,0]
]]
function MazeSolver(maze) {
this.maze = maze;
this.traverse = function(column, row) {
console.log(1, this.maze[0][column][row] )
if(this.maze[0][column][row] == 2) {
console.log('1dwffdf',this.maze[0][column][row]);
} else if(this.maze[0][column][row] == 1) {
console.log("At valid position (" + column + ", " + row + ")");
this.maze[0][column][row] = 9;
if(column < this.maze[0].length - 1) {
this.traverse(column + 1, row);
}
if(row < this.maze[0][column].length - 1) {
this.traverse(column, row + 1);
}
if(column > 0) {
this.traverse(column - 1, row);
}
if(row > 0) {
this.traverse(column, row - 1);
}
}
};
};
any ideas?
This code will iterate through the maze and reduce the values to an array of objects containing each col and row of the correct path.
Note Array.prototype.flat is still experimental so the below may not be compatible.
let path = maze.reduce((acc, _,row)=> {
let validpath = _.reduce((acc,__,col)=> ((__)&&acc.push({col,row}), acc),[] );
if(validpath.length > 0) acc.push(validpath);
return acc;
}, []).flat(0);
Note This version uses Array.prototype.concat which can be used in combination with the spread operator as a polyfill for Array.prototype.flat:
let path = [].concat(...maze.reduce((acc, _,row)=> {
let validpath = _.reduce((acc,__,col)=> ((__)&&acc.push({col,row}), acc),[] );
if(validpath.length > 0) acc.push(validpath);
return acc;
}, []));
Try it out:
var a = [[
[0,0,0,0,0],
[1,1,1,1,0],
[0,0,0,0,0],
[0,1,1,1,1],
[0,0,0,0,0]
]];
let maze = a[0];
let path = [].concat(...maze.reduce((acc, _,row)=> {
let validpath = _.reduce((acc,__,col)=> ((__)&&acc.push({col,row}), acc),[] );
if(validpath.length > 0) acc.push(validpath);
return acc;
}, []));
console.log(path);
Note: Keep in mind that the col and row values are based on indices of the array, which begin at 0.

How to get the index of match string in JS

I am trying to get the index of the exact search string, I have built a function that returns the index the match strings where I need to get the index only with the exact match
here is my function
getIndicesOf = (searchStr, str) => {
var searchStrLen = searchStr.length;
if (searchStrLen === 0) {
return [];
}
var startIndex = 0,
index,
indices = [];
while ((index = str.indexOf(searchStr, startIndex)) > -1) {
indices.push(index);
startIndex = index + searchStrLen;
}
console.log("detercting " , indices );
return indices;
};
console.log(getIndicesOf("go" , "go, I am going ")); //  [0, 9]
here I go the index of go and going , How can get the index only of the exact match string?
The first occurrence of go also contains a comma. So it is not an exact match.
If you still want to get all the indices of go and go, in the words array, you can use the following script.
var x = "go, I am going go";
arr = x.split(" ");
arr.map((e, i) => (e === "go" || e === "go,") ? i : '').filter(String)
If you need to find the index in the string you can use the below approach
var x = "go, I am going go";
arr = x.split(" "); var index = 0;
arr.map((e, i) => {
var occur = (e === "go" || e === "go,") ? index : '';
index+=e.length+1;
return occur}).filter(String)
replace your while loop with this code,
while ((index = str.indexOf(searchStr, startIndex)) > -1) {
if(str.substring(startIndex,searchStrLen) == searchStr)
{
indices.push(index);
startIndex = index + searchStrLen;
}
}

Order string match results by max match

I want to search all match in a string and return all result ordered by max match results, let's say I have some strings:
var strArray = [
"This is my number one string",
"Another string that contains number",
"Just for example string"
];
// Results of search "another number" should be:
var resultArrayOfIndexes = [1, 0];
So far I can search in a string but it returns all indexes where is at least one match, but I want the result array to be sorted by max count of matches.
My code:
function findMatch(list, phrase) {
var preparedList = [],
value = "";
if (config.get("list").match.enabled) {
for (var i = 0, length = list.length; i < length; i += 1) {
value = config.get("getValue")(list[i]);
var words = phrase.split(' ');
var listMatchArr = [];
$.each(words, function(idx, word) {
var W = word.replace(/[\W_]+/g, ""); // match on alphaNum chars only
if (match(value, W) && $.inArray(i, listMatchArr) == -1) { //phrase
preparedList.push(list[i]);
listMatchArr.push(i);
};
});
}
} else {
preparedList = list;
}
return preparedList;
}
I'm assuming a case-insensitive search is required.
The following code changes the phrase into an array of individual words, then maps the list to get back an array of objects in the form {index: 0, matches:1}, then filters out the ones where there were no matches, then sorts, then maps again to get just the indices.
function findMatch(list, phrase) {
var searchTerms = phrase.toLowerCase().split(/\s+/);
return list.map(function(v, i) {
v = v.toLowerCase();
return {
index: i,
matches: searchTerms.reduce(function(a, c) {
return a + (v.indexOf(c) !=-1 ? 1 : 0);
}, 0)
};
})
.filter(function(v) { return v.matches > 0; })
.sort(function(a, b) { return b.matches - a.matches; })
.map(function(v) { return v.index; });
}
var strArray = [
"This is my number one string", "Another string that contains number","Just for example string"
];
console.log(findMatch(strArray, "another number"));
Or expand the following for basically the same thing with ES6 features:
function findMatch(list, phrase) {
var searchTerms = phrase.toLowerCase().split(/\s+/);
return list.map(function(v, i) {
v = v.toLowerCase();
return {
index: i,
matches: searchTerms.reduce((a, c) => a + (v.includes(c) ? 1 : 0), 0)
};
})
.filter(v => v.matches > 0)
.sort((a, b) => b.matches - a.matches)
.map(v => v.index);
}
var strArray = [
"This is my number one string", "Another string that contains number","Just for example string"
];
console.log(findMatch(strArray, "another number"));
You can use regex to match your phrase as well as count how many words are matches in your string if you are familiar with regex.
Assume that you want to know how many words were matched as well, you can store it as an array of objects where each object store the count number and the target string.
var strArray = [
"This is my number one string", "Another string that contains number", "Just for example string"
];
function findMatch(list, phrase){
var words = phrase.split(" ");
var pattern = "";
var length = words.length;
// create pattern for regex match
for(var i = 0; i < length; i++){
pattern += words[i];
if(i < length-1){
pattern += "|";
}
}
var counts = [];
var re = new RegExp(pattern,"g");
for(var i = 0; i < list.length; i++){
var count = (list[i].toLowerCase().match(re) || []).length;
//add to array if matched
if(count > 0){
counts.push({count:count,string:list[i]});
}
}
//sort by max match
counts.sort(function(a,b){
return b.count-a.count;
});
console.log(counts);
}
findMatch(strArray, "another number");
The result will look something like:
[ { count: 2, string: 'Another string that contains number' },
{ count: 1, string: 'This is my number one string' },
{ count: 0, string: 'Just for example string' } ]

search javascript string and get word index instead of char

I would like to search in javascript string and get all string occurrence by word index for example:
var str = 'Hello this is my this is world'
myFindWordIndex(str, 'this is') ==> [1, 4]
(two occurrences of the search string, one starts at word index 1 and one starts at index 4)
the solution can use JQuery
I would split the phrase you're trying to find and where you're trying to find it into words. Then simply check if the phrase contains each piece of the search phrase.
function check(hay, needle, from) {
var i = 1;
while (i < needle.length) {
if (hay[from] != needle[i])
return false;
i++;
from++;
}
return true;
}
function myFindWordIndex(str, findme) {
var indices = [];
var needle = findme.split(" ");
var hay = str.split(" ");
for (var i = 0; i < hay.length - needle.length; i++) {
if (hay[i] == needle[0] && (needle.length==1||check(hay, needle, i)))
indices.push(i);
}
return indices;
}
var str = 'Hello this is my this is world';
console.log(myFindWordIndex(str, 'this is')); // ==> [1, 4]
Here's a clunky solution using Lodash.js.
function run(str, searchingFor) {
return _.flatten(
_.zip(str.split(/\s+/), str.split(/\s+/))
)
.slice(1, -1)
.join(' ')
.match(/\w+\s+\w+/g)
.reduce((a, b, i) => {
return b === searchingFor
? a.concat(i)
: a;
}, []);
}
Here is it running...
run('Hello this is my this is world', 'this is');
// => [1, 4]
Not ideal. Not very portable. But it works.
using function from How to find indices of all occurrences of one string in another in JavaScript? for multi search
function getIndicesOf(searchStr, str, caseSensitive) {
var startIndex = 0, searchStrLen = searchStr.length;
var index, indices = [];
if (!caseSensitive) {
str = str.toLowerCase();
searchStr = searchStr.toLowerCase();
}
while ((index = str.indexOf(searchStr, startIndex)) > -1) {
indices.push(index);
startIndex = index + searchStrLen;
}
return indices;
}
function myFindWordIndex(str, search_str) {
var res = [];
$.each(getIndicesOf(search_str, str, true), function(i, v) {
res.push(str.slice(0, v).split(' ').length)
});
return res;
}
adding #Mohammad's answer since it looks the cleanest:
var str = 'Hello this is my this is world'
var pos = myFindWordIndex(str, 'this is');
console.log(pos);
function myFindWordIndex(str, word){
var arr = [];
var wordLength = word.split(" ").length;
var position = 0;
str.split(word).slice(0, -1).forEach(function(value, i){
position += value.trim().split(" ").length;
position += i > 0 ? wordLength : 0;
arr.push(position);
});
return arr;
}

Javascript sort alphabetically matching the beginning of string then alphabetically for contained text

I need help sorting through some data.
Say I type "piz" in a searchfield. I get in return and array with all the entries that contain "piz".
I now want to display them in the following order:
pizza
pizzeria
apizzetto
berpizzo
First the items that start with what I typed in alphabetical order then the ones that contain what I typed in alphabetical order.
Instead if I sort them alphabetically I get the following
apizzetto
berpizzo
pizza
pizzeria
Does anyone know how to do this?
Thanks for your help.
You can split the data into two arrays, one that starts with your input and one that doesn't. Sort each separately, then combine the two results:
var data = [
'pizzeria',
'berpizzo',
'apizzetto',
'pizza'
];
function sortInputFirst(input, data) {
var first = [];
var others = [];
for (var i = 0; i < data.length; i++) {
if (data[i].indexOf(input) == 0) {
first.push(data[i]);
} else {
others.push(data[i]);
}
}
first.sort();
others.sort();
return(first.concat(others));
}
var results = sortInputFirst('piz', data);
You can see it work here: http://jsfiddle.net/jfriend00/nH2Ff/
The right full solution is:
var data = [
'pizzeria',
'berpizzo',
'apizzetto',
'pizza'
];
var _sortByTerm = function (data, term) {
return data.sort(function (a, b) {
return a.indexOf(term) < b.indexOf(term) ? -1 : 1;
});
};
var result = _sortByTerm(data, 'piz');
If you want object sort, use this function:
var _sortByTerm = function (data, key, term) {
return data.sort(function (a, b) {
return a[key].indexOf(term) < b[key].indexOf(term) ? -1 : 1;
});
};
Using reduce:
const data = ['pizzeria', 'berpizzo', 'pizza', 'apizzetto'];
function sortInputFirst(input, data) {
data.sort();
const [first, others] = data.reduce(([a, b], c) => (c.indexOf(input) == 0 ? [[...a, c], b] : [a, [...b, c]]), [[], []]);
return(first.concat(others));
}
const output = sortInputFirst('piz', data);
console.log(output)
Here's another one:
var str = 'piz';
var arr = ['apizzetto','pizzeria','berpizzo','pizza'];
arr.sort(function(a,b) {
var bgnA = a.substr(0,str.length).toLowerCase();
var bgnB = b.substr(0,str.length).toLowerCase();
if (bgnA == str.toLowerCase()) {
if (bgnB != str.toLowerCase()) return -1;
} else if (bgnB == str.toLowerCase()) return 1;
return a < b ? -1 : (a > b ? 1 : 0);
});
console.log(arr);
Here's how I used the top voted answer to make my search function use a normalized set of data. It removes accents before comparing strings and is also case insensitive.
function getAutocompleteNormalizedMatches(userInput, array) {
const normalizedInput = getNormalizedString(userInput);
let normalizedListItem;
let startsWith = [];
let anywhere = [];
for (let i = 0; i < array.length; i++) {
normalizedListItem = getNormalizedString(array[i]);
if (normalizedListItem.indexOf(normalizedInput) === 0) {
startsWith.push(array[i])
} else if (normalizedListItem.includes(normalizedInput)) {
anywhere.push(array[i])
}
}
startsWith.sort();
anywhere.sort();
return startsWith.concat(anywhere);
}
const getNormalizedString = function (str) {
str = str.replace(/\s+/g, " ").trim();
return (str ? removeDiacritics(str.toLowerCase()) : "");
};
To get the removeDiacritics() function, please refer to this link because it takes quite a bit of useless space if you guys don't need it: Remove accents diacritics in a string with JavaScript

Categories

Resources