Find path between 2 countries with borders - javascript

I Have a JSON with countries from a continent. Each country has a array that has it's border countries. Given 2 countries from the same continent, return it's route. E.g.:
Brazil and USA -> 1. Colômbia, 2. Panamá, 3. Costa Rica, 4. Nicarágua, 5. Honduras,
6. Guatemala, 7. México.
I'm struggling to organize it on one array only to make a BFS (Breadth First Search). What i have until now is:
function traverse(main_key, obj) {
obj.forEach(function(key) {
if (visited[main_key] === undefined) {
visited[main_key] = new Array()
}
visited[main_key].push(obj)
if (typeof(borders[key]) === 'array' && visited[main_key].indeOf(key) !== -1) {
traverse(borders[key])
}
else {
//do something with the actual value
console.log(main_key, borders[key], key)
}
})
};
traverse('BRA', borders['BRA'])
Here is JSON sample: https://restcountries.eu/rest/v2/region/americas
I think my main problem is i'm struggling with transforming this JSON into a GRAPH.

Checkout Dijkstra's algorithm where you can assume that countries as nodes in the algorithm and bordering countries have a distance of 1 and non-bordering countries don't have borders.
Edit:
Here is an implementation using the data you specified. Pass the data you get from the JSON sample (entire array) as the graph parameter, source country (for example 'BRA'), target country (for example 'USA'). I didn't do a lot of testing, so I can't guarantee this is bug free.
function dijkstra(graph, source, target) {
var dist = {};
var prev = {};
var vertices = [];
for (var i = 0; i < graph.length; i++) {
var vertex = graph[i];
dist[vertex.alpha3Code] = Infinity;
prev[vertex.alpha3Code] = null;
vertices.push(vertex);
}
dist[source] = 0;
while (vertices.length > 0) {
// Find the vertex having smallest dist.
var u = vertices.reduce(function(p, current) {return !p || dist[current.alpha3Code] < dist[p.alpha3Code] ? current : p;}, null);
const index = vertices.indexOf(u);
vertices.splice(index, 1);
if (u.borders && Array.isArray(u.borders)) {
for (var i = 0; i < u.borders.length; i++) {
var v = u.borders[i];
const alt = dist[u.alpha3Code] + 1;
if (alt < dist[v]) {
dist[v] = alt;
prev[v] = u;
}
}
}
}
var result = [];
while (target) {
result.splice(0, 0, target);
target = prev[target] ? prev[target].alpha3Code : null;
}
// Any empty array return means there is no path. For example one of the countries is an island.
if (result.length === 1) {
return [];
}
return result;
}

Related

Javascript Importing images into Photoshop by its alphabetical and numerical order

This is a javascript (not mine) that imports images into Photoshop by its alphabetical and numerical order.
When script runs:
-User selects folder of images to import into Photoshop.
-Images then imported in by its alphabetical and numerical order. EX:
a.01.png, b.01.png, c.01.png, a.02.png, b.02.png, c.02.png, a.03.png,
b.03.png, c.03.png
..etc.
Script works great until it gets to the end. It always fails to bring in one of the images from the last numerical set. EX of failure:
a.01.png, b.01.png, c.01.png, a.02.png, b.02.png, c.02.png,
“Skips bringing in a.03.png here”
b.03.png, c.03.png
Doesn’t matter how many number of sets, the very last set always fails to import one image. The rest import fine. I haven’t been able to figure out what causes this. Anybody able to find the issue in this script below?
Update
I didn't mention that this is a segment of code out of a much larger JavaScript so the PSDcreate and other items are used elsewhere. I just cropped out the segment that was failing since the original is too large to post. I ran the script you created and it does bring them in but not quite the way they do in the code it I have. I believe yours brings in the images
a.00.1png, a.002.png, a.003.png b.001.png, b.002.png, b.003.png, c.001.png, c.002.png, c.003.png...ect
The one I am using puts in in order of
a.001.png, b.001.png, c.001.png, a.002.png, b.002.png, c.002png
...etc
I have the a,b,c images ending in .001 imported into Photoshop first. Then at that point the code I took out then stacks the images in order (a,b,c) and does various other tasks. When done, saves them out as a PSD file "name.001.psd". Then the script brings in the next group of images with 002 and repeats the process in a loop. That's the part I removed because its so much code and didn't seem to be the issue. Is it possible to have the images brought in by
a,b,c of .001, then a,b,c of .002..etc?
Code:
#target photoshop
app.bringToFront();
// Dialog for user to choose folder of documents to process
var inputFolderArray = [];
do {
var inputFolder = Folder.selectDialog("Select a folder of documents to process");
if(inputFolder != null) {
inputFolderArray.push(inputFolder);
}
}
while(inputFolder != null
|| inputFolder != undefined)
// Pulls images from inputFolder
for (var j = 0; j < inputFolderArray.length; j++) {
var filesList = inputFolderArray[j].getFiles();
var outputDirectory = inputFolderArray[j] + '/';
// Sort the order of files corresponding to the number
filesList.sort(function(x,y) {
// the substr gets the numbers (ex: get "01" from image.01.png)
var xp = (x.name).substr(-6, 2);
var yp = (y.name).substr(-6, 2);
return parseInt(xp) - parseInt(yp);
});
var frameArrays = [[]];
var oldFrameNum = (filesList[0].name).substr(-6, 2);
// These are used for array slice
var arrayStartNum = 0;
var arrayEndNum = 1;
// Put each frame into separate array
for (var i = 1; i < filesList.length; i++) {
var currentFrameNum = (filesList[i].name).substr(-6, 2);
if(oldFrameNum !== currentFrameNum) {
oldFrameNum = currentFrameNum;
frameArrays[0].push(filesList.slice(arrayStartNum, i));
arrayStartNum = i;
arrayEndNum = i-1;
}
else if(i === filesList.length-1) {
frameArrays[0].push(filesList.slice(arrayStartNum, i));
}
}
for (var i = 0; i < frameArrays[0].length; i++) {
// Sort the file order alphabetically
sorter = MySort('*!#_.()#^&%-=+01234567989abcdefghijklmnopqrstuvwxyz');
frameArrays[0][i].sort(sorter)
PSDCreate(frameArrays[0][i], outputDirectory);
}
}
// FUNCTIONS BELOW//
function PSDCreate(frameArrays, outputDirectory) {
directory = outputDirectory + '/';
//var outputLocation = inputFolder + "/" + directory;
var outputFileName = '';
if (frameArrays != null) {
// Get all the files in the folder
var fileList = frameArrays;
var k = 0;
for (var i = 0; i < fileList.length; i++) {
if (fileList[i] instanceof File && fileList[i].hidden == false) {
var fileName = fileList[i].name;
var docRef = open(fileList[i]);
if(k == 0) {
k++;
outputFileName = RemoveExtension(docRef.name);
}
}
}
}
}
// Removes extension from file name
function RemoveExtension(name) {
var fileNameNoExtension = name;
fileNameNoExtension = fileNameNoExtension.split(".");
if ( fileNameNoExtension.length > 1 ) {
fileNameNoExtension.length--;
}
fileNameNoExtension = fileNameNoExtension.join(".");
return fileNameNoExtension;
}
// Sort the file order alphabetically with special characters
function MySort(alphabet)
{
return function(a, b) {
var index_a = alphabet.indexOf(a[0]),
index_b = alphabet.indexOf(b[0]);
if (index_a === index_b) {
// same first character, sort regular
if (a < b) {
return -1;
} else if (a > b) {
return 1;
}
return 0;
} else {
return index_a - index_b;
}
}
}
I wasn't able to figure out why you lose one of the array elements: your sorting was quite puzzling to me, so I rewrote sort, I think it's a bit easier to read. I first extract all the information I need to sort from file path (prefix, number) and put it to a temporary array (along with anything I need to open this path later). The I sort this array by prefix and number. Also I replaced PSDCreate with app.open because question was about opening files in a specific order, PSDCreate contains a lot of irrelevant info.
var filesArray = [],
parsedFilesArray = [];
// Dialog for user to choose folder of documents to process
var inputFolderArray = [];
do {
var inputFolder = Folder.selectDialog("Select a folder of documents to process");
if (inputFolder != null)
{
inputFolderArray.push(inputFolder);
}
}
while (inputFolder != null ||
inputFolder != undefined)
// create an array of objects to sort it later based on files from InputFolder
for (var j = 0; j < inputFolderArray.length; j++)
{
var filesList = inputFolderArray[j].getFiles();
for (var i = 0; i < filesList.length; i++)
{
//extracting values for an object: [1] == path, [2] == name, [3] == number;
var fileString = String(filesList[i]).match(/(.*)(?:\/)(.*?)\.(\d*)\.(.*)/);
parsedFilesArray.push(
{
key: fileString[2], //filename
num: fileString[3], //01
path: fileString[1], // c/path/to/file/
file: [fileString[2], fileString[3], "png"].join(".") // filename.01.png
})
}
}
//sorting the array
parsedFilesArray.sort(function(a, b)
{
if (a.key === b.key)
{
return parseInt(a.num) - parseInt(b.num);
}
return a.key > b.key ? 1 : -1;
})
//opening files from the sorted array
for (var i = 0; i < parsedFilesArray.length; i++)
{
app.open(new File(parsedFilesArray[i].path + "/" + parsedFilesArray[i].file))
}

Finding shortest path unweighted graph using BFS (javascript)

I'm trying to apply BFS to find the length of the shortest path in a graph, but am not quite getting the right result.
I try to find the shortest path by visiting each node in the graph; then mark the ones that are visited, and continue recording the length of the path. What I hope to return is an array that contains the shortest path, but I think I am doing something wrong in the process.
I think this has something to do with how I am indexing my arrays and recording my distances.
My input is currently formatted in the form of an array that contains the neighbors for each vertex i. So, for instance, graph[i] would give you an array of neighbors of vertex i.
Any thoughts on how I can go about fixing my issue would be very helpful. Thanks!
var shortestPathLength = function(graph) {
let distances = []
let pq = []
distances[0] = 0
let mygraph = {}
for (var i = 0; i<graph.length; i++) {
distances[i] = -1
mygraph[i] = graph[i]
}
pq.push(mygraph[0])
while(pq.length > 0) {
let min_node = pq.shift()
for(var i = 0; i<min_node.length; i++) {
candidate = distances[i] + 1
if(distances[min_node[i]]== -1) {
distances[min_node[i]] = distances[i] + 1
pq.push(graph[min_node[i]])
}
else if (candidate < distances[min_node[i]]) {
distances[min_node[i]] = distances[i] + 1
}
}
}
function getSum(total, num) {
return total + num;
}
console.log(distances)
return distances.length
};
Your problem is candidate = distances[i] + 1. The i is the index of the edge inside the min_node, which isn't interesting at all. What you are looking for is the current distance to the min_node. You will need to either assign the distance as a property of the min_node object itself, or you will need to store the id (index in graph) of the nodes in your queue instead of the object itself.
I've made a few other simplifications, but the only showstopper problem in your code was the distance lookup.
function shortestPathLength = function(graph) {
const distances = Array(graph.length).fill(-1);
distances[0] = 0; // start node
const queue = [0];
while (queue.length > 0) {
const node_index = queue.shift();
// ^^^^^
const edges = graph[node_index]; // get the node itself
const candidate = distances[node_index] + 1; // outside of the loop
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
for (const target in edges) {
if (distances[target] == -1) {
distances[target] = candidate;
queue.push(target); // not graph[target]
// ^^^^^^
}
}
}
return distances;
}

Getting Incorrect range height for seemingly no reason?

I am writing a script to copy and paste a range from one sheet to another. The pasted range size should be reduced by using two functions : one to delete rows with specific values and the other is an aggregate function.
I started getting this error after I introduced the aggregate function The function is basically reducing the array size using the reduce JS function.
I have replicated my problem here and the code is accessible in the script editor.
When I run the script I am getting the following error :
Incorrect range height was 28 but should be 7 (line 36, file "test")
I have no idea why am I getting this error. My aggregate function returns a properly formatted array with the right length.
function append_range(){
var origin_sheet = SpreadsheetApp.openById('1-2ZheMz1p01qwtwY3ghbNjJedYfGXeylnLEjDMCLpMw');//open the file
origin_sheet = origin_sheet.getSheetByName('test');
var rangeStart = 2;
var range = origin_sheet.getRange('A'+ (rangeStart.toString())+':T'+ (origin_sheet.getLastRow()).toString());
var dataFromRange = range.getValues();
var dataFromRangeLength = dataFromRange.length;
var destination_sheet = SpreadsheetApp.openById('1-2ZheMz1p01qwtwY3ghbNjJedYfGXeylnLEjDMCLpMw');
destination_sheet = destination_sheet.getSheetByName('append');
var rowLast = destination_sheet.getLastRow()+1;
Logger.log("row last" + rowLast);
var formattedRange = deleteRows(dataFromRange);
var groups = aggregate(formattedRange);
var aggregates = [];
for(var group in groups)
{
aggregates.push(groups[group]);
}
Logger.log(aggregates);
var formattedRangeLength = aggregates.length;
Logger.log("formattedRangeLength" + formattedRangeLength);
destination_sheet.getRange(rowLast,1,formattedRangeLength, 20).setValues(deleteRows(dataFromRange));
function isDate(sDate) {
if (isValidDate(sDate)) {
sDate = Utilities.formatDate(new Date(sDate), "PST", "yyyy-MM-dd");
}
return sDate;
}
function isValidDate(d) {
if ( Object.prototype.toString.call(d) !== "[object Date]" )
return false;
return !isNaN(d.getTime());
}
//
function deleteRows(dataRange){//just pass the range in an array and this method will return another array with filtered range
var formatted = dataRange.filter(function(e) {
return e[8]||e[9]||e[10]||e[11]||e[12]||e[13]||e[14]||e[15]||e[16]||e[17]||e[18]||e[19];
});
return formatted;
}
function aggregate(data)
{
var groups = data.reduce(
function(accumulator, previous){
{
var key = previous[1] + previous[3] + previous[5] + previous[6];
var group = accumulator[key];
if(group == null || typeof group == 'undefined')
{
accumulator[key] = previous;
}
else {
var startIndex = 8;
for(var i = startIndex; i < previous.length;i++)
{
group[i] += previous[i];
}
}
return accumulator;
}},
{});
return groups;
}
}
The .setValues() is not setting your aggregates array it is trying to set deleteRows(dataFromRange)
// Change the setValues() to your reduced array
destination_sheet.getRange(rowLast,1,formattedRangeLength, 20).setValues(aggregates);
I think this might work:
var output=deleteRows(dataFromRange));
destination_sheet.getRange(rowLast,1,output.length, output[0].length).setValues(deleteRows(output));
This assumes a non jagged array.

How to compare every number in an array against each other? (javascript)

I have a set of numbers which are displayed like followed;
var data = "615:415,600:400,600:400,300:300"
Each number represents an x/y coordinate, and I would like to add a value next to each one which is calculated based on the frequency of the number within a range.
So, I would like to be able to compare each value against all others in this string, and from this perform the following functions;
Remove the number from the string if it is a duplicate, and add :1
If the x/y numbers are both within a range of 15 against any other number, add:1
If there are no matches, add :0
Turn into array
So using the data string, it would be transformed to;
var data = "615:415:1, 600:400:2, 300:300:0"
I have been trying to do this using a reducer function, but I'm struggling with mainly step 2. I'm hoping someone can help out?
Thanks - Code + Plunk below!
http://plnkr.co/edit/zPW1844cLnUFAlEI77jq?p=preview
var result = [];
var data = "615:415,600:400,600:400,300:300"
var count = 0;
var reducer = function(p, c, i, a) {
if (p && p !== c) {
var _t = p.split(":");
result.push({
x: _t[0],
y: _t[1],
value: count
});
count = 0;
if (i === a.length - 1) {
_t = c.split(":");
result.push({
x: _t[0],
y: _t[1],
value: count
});
}
}
else {
count++;
}
return c
}
data.split(',').sort().reduce(reducer);
console.log(result)
You could use a step-by-step approach and split the string first in coordinates, generate a hash table for the coordinates with count and filter only unique coordinates.
Then compare each unique coordinates with each other and count if inside of a given range.
Later map the coordinates with the count and join to string.
var data = "615:415,600:400,600:400,300:300",
result = function (array) {
var i, j,
hash = Object.create(null),
unique = array.split(',').filter(function (a) {
var parts = a.split(':');
if (!hash[a]) {
hash[a] = [parts[0], parts[1], 0]; // [x, y, count]
return true;
}
hash[a][2]++;
});
for (i = 0; i < unique.length - 1; i++) {
for (j = i + 1; j < unique.length; j++) {
if (
Math.abs(hash[unique[i]][0] - hash[unique[j]][0]) <= 15 &&
Math.abs(hash[unique[i]][1] - hash[unique[j]][1]) <= 15
) {
hash[unique[i]][2]++;
hash[unique[j]][2]++;
}
}
}
return unique.map(function (a) {
return hash[a].join(':');
}).join(', ');
}(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's an alternative:
var data = "615:415,600:400,600:400,300:300";
var result = (function (s) {
var result = {};
var values = [];
// Process each value
s.split(',').forEach(function (v) {
var b = v.split(':');
// If a match, increment count by 2 (once for match and again for within 15)
if (result[v]) {
result[v].count += 2;
// Otherwise, just check for within 15
} else {
result[v] = {x:b[0], y:b[1], count:0};
values.forEach(function(xy, i){
if (xy[0]>= (b[0]-15) && xy[0] <= (+b[0]+15) &&
xy[1]>= (b[1]-15) && xy[1] <= (+b[1]+15) ) {
++result[xy.join(':')].count; // Increment for nearby only
}
})
values.push([b[0],b[1]]);
}
})
// Create required string format
return Object.keys(result).reduce(function(arr, key){
arr.push(key + ':' + result[key].count);
return arr;
},[]).join(', ');
})(data)
console.log(result);
All answers so far are good. I just would like to introduce a little variety by inventing an Array.prototype.withEachOther() method. Which just takes a callback an invokes the callback with each other item of the array being it's arguments as you may suggest. It works in place.
Array.prototype.withEachOther = function(cb){
this.map(function(e,i,a){
var t = a.slice();
t.splice(0,i+1);
t.map(function(f){
a[i] = cb(e,f);
});
});
return this;
};
var data = "615:415,600:400,600:400,300:300, 550 : 550".split(/\s*,\s*/)
.map(s => s.split(/\s*:\s*/).concat(0)),
cb = (f,s) => (Math.abs(f[0]-s[0]) <= 15 && Math.abs(f[1]-s[1]) <= 15 && (f[2]++, s[2]++),f);
result = data.reduceRight(function(p,c,i,a){
var fi = a.slice(0,i-a.length)
.findIndex(f => f[0] === c[0] && f[1] === c[1]);
fi !== -1 ? (a[fi][2] += ++c[2], a.splice(i,1))
: p.push(c);
return p;
},[])
.withEachOther(cb)
.reduce((p,c) => p += c[0]+":"+c[1]+":"+c[2]+", ","");
console.log(result);

Javascript sort and order

So i have this array
[ 'vendor/angular/angular.min.js',
'vendor/angular-nice-bar/dist/js/angular-nice-bar.min.js',
'vendor/angular-material/modules/js/core/core.min.js',
'vendor/angular-material/modules/js/backdrop/backdrop.min.js',
'vendor/angular-material/modules/js/dialog/dialog.min.js',
'vendor/angular-material/modules/js/button/button.min.js',
'vendor/angular-material/modules/js/icon/icon.min.js',
'vendor/angular-material/modules/js/tabs/tabs.min.js',
'vendor/angular-material/modules/js/content/content.min.js',
'vendor/angular-material/modules/js/toolbar/toolbar.min.js',
'vendor/angular-material/modules/js/input/input.min.js',
'vendor/angular-material/modules/js/divider/divider.min.js',
'vendor/angular-material/modules/js/menu/menu.min.js',
'vendor/angular-material/modules/js/select/select.min.js',
'vendor/angular-material/modules/js/radioButton/radioButton.min.js',
'vendor/angular-material/modules/js/checkbox/checkbox.min.js',
'vendor/angular-material/modules/js/switch/switch.min.js',
'vendor/angular-material/modules/js/tooltip/tooltip.min.js',
'vendor/angular-material/modules/js/toast/toast.min.js',
'vendor/angular-clipboard/angular-clipboard.js',
'vendor/angular-animate/angular-animate.min.js',
'vendor/angular-aria/angular-aria.min.js',
'vendor/angular-messages/angular-messages.min.js',
'vendor/angular-ui-router/release/angular-ui-router.js',
'src/app/about/about.js',
'src/app/hekate.cfg.js',
'src/app/hekate.ctrl.js',
'src/app/hekate.module.js',
'src/app/home/home.js',
'src/app/user/dialog/user.signIn.ctrl.js',
'src/app/user/dialog/user.signIn.module.js',
'src/app/user/user.cfg.js',
'src/app/user/user.ctrl.js',
'src/app/user/user.module.js',
'src/common/services/toast.service.js',
'templates-common.js',
'templates-app.js'
]
And taking the following part from the above array as example:
[
'src/app/hekate.cfg.js',
'src/app/hekate.ctrl.js',
'src/app/hekate.module.js',
]
I want to sort it like
[
'src/app/hekate.module.js',
'src/app/hekate.cfg.js',
'src/app/hekate.ctrl.js',
]
So more specific of what i want is to find in that array where string is duplicated and after check if has at the end [.cfg.js, .ctrl.js, .module.js] and automatic order them to [.module.js, .cfg.js, .ctrl.js]
Can anyone please help me with that?
A single sort proposal.
var array = ['src/app/about/about.js', 'src/app/hekate.cfg.js', 'src/app/hekate.ctrl.js', 'src/app/hekate.module.js', 'src/app/home/home.js', 'src/app/user/dialog/user.signIn.ctrl.js', 'src/app/user/dialog/user.signIn.module.js', 'src/app/user/user.cfg.js', 'src/app/user/user.ctrl.js', 'src/app/user/user.module.js'];
array.sort(function (a, b) {
function replaceCB(r, a, i) { return r.replace(a, i); }
var replace = ['.module.js', '.cfg.js', '.ctrl.js'];
return replace.reduce(replaceCB, a).localeCompare(replace.reduce(replaceCB, b));
});
document.write('<pre>' + JSON.stringify(array, 0, 4) + '</pre>');
To prevent so much replaces, i suggest to have a look to sorting with map.
You can try something like this:
Algo:
Group based on path and store file names as value.
Check for existence of one of special file ".cfg.js"
Sort following list based on custom sort.
Loop over object's property and join key with values to form full path again.
If you wish to sort full array, you can sort keys itself and then merge path with names. I have done this. If you do not wish to do this, just remove sort function from final loop.
Sample
var data=["vendor/angular/angular.min.js","vendor/angular-nice-bar/dist/js/angular-nice-bar.min.js","vendor/angular-material/modules/js/core/core.min.js","vendor/angular-material/modules/js/backdrop/backdrop.min.js","vendor/angular-material/modules/js/dialog/dialog.min.js","vendor/angular-material/modules/js/button/button.min.js","vendor/angular-material/modules/js/icon/icon.min.js","vendor/angular-material/modules/js/tabs/tabs.min.js","vendor/angular-material/modules/js/content/content.min.js","vendor/angular-material/modules/js/toolbar/toolbar.min.js","vendor/angular-material/modules/js/input/input.min.js","vendor/angular-material/modules/js/divider/divider.min.js","vendor/angular-material/modules/js/menu/menu.min.js","vendor/angular-material/modules/js/select/select.min.js","vendor/angular-material/modules/js/radioButton/radioButton.min.js","vendor/angular-material/modules/js/checkbox/checkbox.min.js","vendor/angular-material/modules/js/switch/switch.min.js","vendor/angular-material/modules/js/tooltip/tooltip.min.js","vendor/angular-material/modules/js/toast/toast.min.js","vendor/angular-clipboard/angular-clipboard.js","vendor/angular-animate/angular-animate.min.js","vendor/angular-aria/angular-aria.min.js","vendor/angular-messages/angular-messages.min.js","vendor/angular-ui-router/release/angular-ui-router.js","src/app/about/about.js","src/app/hekate.cfg.js","src/app/hekate.ctrl.js","src/app/hekate.module.js","src/app/home/home.js","src/app/user/dialog/user.signIn.ctrl.js","src/app/user/dialog/user.signIn.module.js","src/app/user/user.cfg.js","src/app/user/user.ctrl.js","src/app/user/user.module.js","src/common/services/toast.service.js","templates-common.js","templates-app.js"];
// Create groups based on path
var o = {};
data.forEach(function(item) {
var lastIndex = item.lastIndexOf('/') + 1;
var path = item.substring(0, lastIndex);
var fname = item.substring(lastIndex);
if (!o[path]) o[path] = [];
o[path].push(fname);
});
var manualOrder= [".module.js", ".cfg.js", ".ctrl.js"];
Array.prototype.fuzzyMatch = function(search){
return this.some(function(item){
return item.indexOf(search)>-1;
});
}
Array.prototype.fuzzySearchIndex = function(search){
var pos = -1;
this.forEach(function(item, index){
if(search.indexOf(item)>-1){
pos = index;
}
});
return pos;
}
function myCustomSort(a,b){
var a_pos = manualOrder.fuzzySearchIndex(a);
var b_pos = manualOrder.fuzzySearchIndex(b);
return a_pos > b_pos ? 1 : a_pos < b_pos ? -1 : 0;
}
// Check for ".cfg.js" and apply custom sort
for (var k in o) {
if (o[k].fuzzyMatch(".cfg.js")) {
o[k].sort(myCustomSort);
}
}
// Merge Path and names to create final value
var final = [];
Object.keys(o).sort().forEach(function(item) {
if (Array.isArray(o[item])) {
final = final.concat(o[item].map(function(fn) {
return item + fn
}));
} else
final = final.concat(o[item]);
});
console.log(final);
First make an array for names like 'hekate'.
Then make an array for final results.
We need 3 searching loops for ctrls, cfgs and modules.
If string contains arrayWithNames[0] + '.module' push the whole record to new array that you created. Same with ctrls and cfgs.
var allItems = []; //your array with all elements
var namesArray = [];
var finalResultsArray = [];
//fill name array here:
for(var i=0; i<=allItems.length; i++){
//you have to split string and find the module name (like 'hekate'). i hope you know how to split strings
}
//sort by modules, cfgs, ctrls:
for(var i=0; i<=namesArray.length; i++){
if(allItems[i].indexOf(namesArray[i] + '.module') > -1) {
finalResultsArray.push(allItems[i]);
}
}
for(var i=0; i<=namesArray.length; i++){
if(allItems[i].indexOf(namesArray[i] + '.cfg') > -1) {
finalResultsArray.push(allItems[i]);
}
}
for(var i=0; i<=namesArray.length; i++){
if(allItems[i].indexOf(namesArray[i] + '.ctrl') > -1) {
finalResultsArray.push(allItems[i]);
}
}
//now finalResultsArray have what you wanted
You can provide your own compare function to array.sort (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
Write one that returns the correct order for modules, ctrls and cfgs:
It should first remove the suffixes, and if the rest is the same, use the correct logic to return the order according to the suffix. Otherwise return a value according to the alphabetical order.
Update
I didn't test this code (not is it finished), but it should look something like that:
arr.sort(function(a, b) {
if ((a.endsWith(".cfg.js") || a.endsWith(".ctrl.js") || a.endsWith(".module.js")) &&
(b.endsWith(".cfg.js") || b.endsWith(".ctrl.js") || b.endsWith(".module.js"))) {
var sortedSuffixes = {
".module.js": 0,
".cfg.js": 1,
".ctrl.js": 2
};
var suffixAIdx = a.lastIndexOf(".cfg.js");
if (suffixAIdx < 0) suffixAIdx = a.lastIndexOf(".ctrl.js");
if (suffixAIdx < 0) suffixAIdx = a.lastIndexOf(".module.js");
var suffixBIdx = b.lastIndexOf(".cfg.js");
if (suffixBIdx < 0) suffixBIdx = b.lastIndexOf(".ctrl.js");
if (suffixBIdx < 0) suffixBIdx = b.lastIndexOf(".module.js");
var prefixA = a.substring(0, suffixAIdx);
var prefixB = b.substring(0, suffixAIdx);
if (prefixA != prefixB)
{
return a.localeCompare(b);
}
var suffixA = a.substring(suffixAIdx);
var suffixB = b.substring(suffixBIdx);
return sortedSuffixes[suffixA] - sortedSuffixes[suffixB];
} else {
return a.localeCompare(b);
}
});
Update 2
Here is a fiddle (https://jsfiddle.net/d4fmc7ue/) that works.

Categories

Resources