Traverse digits in strings - javascript

I'm trying to consider a function that takes in a flat array of string decimal integers that represent nodes in a tree, each period connotes hierarchy in that tree. I'm trying to create prevNode and nextNode functions. that take three parameters ids, id, planeLock. If a node has no prev or next id then false is returned. If planeLock is true then instead of going to the next node in the tree (e.g. from 1 to 0.1) it will go to the next node in that plane (eg. from 1 to 0) otherwise know as it's sibling, and not it's siblings deepest child.
var ids = [
'0',
'0.1',
'1',
'2',
'2.0',
'2.1',
]
prevNode(ids, '0') -> false // no prev node
prevNode(ids, '1', true) -> 0 // pass true stays on same plane
prevNode(ids, '1') -> 0.1 // prev node in tree
prevNode(ids, '2.0', true) -> false
prevNode(ids, '2.0') -> 2 // goes up one node
How can I parse these strings to get the desired results?

One possible approach:
function getLevel(id) {
return id.split('.').length;
}
function siblingNode(ids, id, planeLock, goesBack) {
var index = ids.indexOf(id);
var level = getLevel(id);
while (goesBack ? --index >= 0 : ++index < ids.length) {
var currEl = ids[index];
var currLevel = getLevel(currEl);
if (!planeLock || currLevel === level) {
return currEl;
}
if (currLevel < level) {
break;
}
}
return false;
}
function prevNode(ids, id, planeLock) {
return siblingNode(ids, id, planeLock, true);
}
function nextNode(ids, id, planeLock) {
return siblingNode(ids, id, planeLock, false);
}
Demo. Apparently, there's a tradeoff between memoizing all the levels (speedy but costs memory) and not (vice versa). If the source array is dynamic, and you'll have to look for a place when inserting new items, I'd strongly recommend memoizing approach (as you'll have to check level at each insertion).

Sorting the whole thing is a good approach. But if you want to extend this with additional functionality it may be better to convert your id list into a tree.
function createSortedTree(ids) {
var tree = {name: "", id: "root", children: {}};
function insert(tree, elem) {
if(!tree.children[elem[0]]) {
tree.children[elem[0]] = {
id: elem[0],
children: {},
parent: tree,
name: tree.id === "root" ? "" + elem[0] : tree.name + "." + elem[0]
};
}
if(elem.length > 1) insert(tree.children[elem[0]], elem.slice(1));
}
for(i in ids) insert(tree, ids[i].split("."));
function traverse(tree) {
if(current) {
current.next = tree;
tree.prev = current;
}
current = tree;
var children = Object.keys(tree.children)
.sort(function(a, b) {if(a < b) return -1; else if(a > b) return 1; else return 0;})
.map(function(key) {return tree.children[key]});
for(i in children) {
if(i > 0) children[i].prevPlane = children[i-1];
if(i < children.length - 1) children[i].nextPlane = children[i+1];
traverse(children[i]);
}
}
var current = null;
traverse(tree);
return tree;
}
function getNode(tree, id) {
if(typeof id === "string") id = id.split(".");
if(id.length === 0) return tree;
else return getNode(tree.children[id[0]], id.slice(1));
}
var tree = createSortedTree(["0", "0.1", "1", "2", "2.0", "2.1"])
var node = getNode(tree, "2.0");
console.log(node.prev.name);
console.log(node.next.name);
var node = getNode(tree, "1");
console.log(node.prev.name);
console.log(node.prevPlane.name);
http://jsfiddle.net/jxyqjq3c/

var _ = require('lodash')
function compare (n1, n2) {
var path1 = n1.split('.')
var path2 = n2.split('.')
var maxLen = Math.max(path1.length, path2.length)
var i = 0
while (i < maxLen) {
if (!path1[i] || +path1[i] < +path2[i]) {
return -1
}
if (!path2[i] || +path1[i] > +path2[i]) {
return 1
}
i++
}
return 0
}
function subset (ids, id) {
return _.filter(ids, function (_id) {
var _idArr = _id.split('.')
var idArr = id.split('.')
var _idChop = _.take(_idArr, _idArr.length - 1).join('.')
var idChop = _.take(idArr, idArr.length - 1).join('.')
if (_idChop === idChop) return true
return false
})
}
function metaInfo (ids, id) {
ids = ids.sort(compare)
var idIndex = ids.indexOf(id)
var meta = {}
meta.prev = (ids[idIndex - 1]) ? ids[idIndex - 1] : false
meta.next = (ids[idIndex + 1]) ? ids[idIndex + 1] : false
var idsSubset = subset(ids, id)
var idSubsetIndex = idsSubset.indexOf(id)
meta.prevSibling = (idsSubset[idSubsetIndex - 1]) ? idsSubset[idSubsetIndex - 1] : false
meta.nextSibling = (idsSubset[idSubsetIndex + 1]) ? idsSubset[idSubsetIndex + 1] : false
return meta
}
var ids = [ '0', '1', '2', '3', '0.0.0', '0.0.1', '0.0', '1.0' ]
var val = metaInfo(ids, '1')
console.log(val)

Here's a possibility. The implementation for nextNode would follow the same approach and re-use most of the function with the exception of changing the way the iterator is behaving.
function prevNode(collection, item, planeLock) {
var iterator = collection.indexOf(item) - 1
if (planeLock) {
while( ~iterator
&& !( item.split('.').length === 1 && collection[iterator].split('.').length === 1)
&& !( item.split('.').length === collection[iterator].split('.').length && item.split('.')[0] === collection[iterator].split('.')[0] ) )
iterator--
return ~iterator ? collection[iterator] : false
} else return collection[iterator] || false
}

Related

Check the sequences in the array JS

I'm tying to solve an "easy" problem from LeetCode, called Divide Array in Sets of K Consecutive Numbers, but can not find a way of how to check the sequences. From my point of view it will be to many loops:
const isPossibleDivide = (nums, k) => {
const sorted = nums.sort((a, b) => a - b)
const counts = {}
sorted.forEach(item => counts[item] = (counts[item] || 0) + 1)
// the problem part
Object.entries(counts).map(([key, value]) => {
if (value !== 0) {
counts[key] = value - 1
}
})
console.log(counts)
}
isPossibleDivide([3, 2, 1, 2, 3, 4, 3, 4, 5, 9, 10, 11], 3)
For this problem, we'd use a map. This'll pass through:
const isPossibleDivide = (nums, k) => {
if (!nums.length % k) {
return false;
}
const headsMap = new Map();
for (const num of nums) {
headsMap.set(num, headsMap.has(num) ? -~headsMap.get(num) : 1);
}
for (let head of nums) {
if (headsMap.get(head) === 0) {
continue;
}
while (headsMap.get(--head) > 0);
++head;
const count = headsMap.get(head);
for (let index = 1; index < k; ++index) {
const curr = headsMap.get(head + index)
if (curr === undefined || curr < count) {
return false;
}
headsMap.set(head + index, curr - count);
}
headsMap.set(head, 0);
}
return true;
};
If we would be able to use deque, this python version would help:
from typing import List
class Solution:
def isPossibleDivide(self, nums: List[int], k: int):
count_map = collections.Counter(nums)
heads_map = collections.deque()
last_checked = -1
opened = 0
for key in sorted(count_map):
if opened > count_map[key] or opened > 0 and key > -~last_checked:
return False
heads_map.append(count_map[key] - opened)
last_checked = key
opened = count_map[key]
if len(heads_map) == k:
opened -= heads_map.popleft()
return opened == 0
In Java, we could use TreeMap and LinkedList:
public class Solution {
public static final boolean isPossibleDivide(int[] nums, int k) {
Map<Integer, Integer> countMap = new TreeMap<>();
for (int num : nums) {
countMap.put(num, -~countMap.getOrDefault(num, 0));
}
Queue<Integer> headsMap = new LinkedList<>();
int lastChecked = -1;
int opened = 0;
for (int key : countMap.keySet()) {
if (opened > 0 && key > -~lastChecked || opened > countMap.get(key)) {
return false;
}
headsMap.add(countMap.get(key) - opened);
lastChecked = key;
opened = countMap.get(key);
if (headsMap.size() == k) {
opened -= headsMap.remove();
}
}
return opened == 0;
}
}
References
For additional details, you can see the Discussion Board. There are plenty of accepted solutions with a variety of languages and explanations, efficient algorithms, as well as asymptotic time/space complexity analysis1, 2 in there.
var isPossibleDivide = (array,k)=>{
const map = new Map()
if(array.length%k !== 0){
return false
}
for(let i = 0 ; i < array.length ; ++i ){
if(map[array[i]]===undefined){
map[array[i]] = 1
}
else{
map[array[i]] = map[array[i]] + 1
}
}
for(let key in map){
x = map[key];
if( x === 0 ){
continue
}
for(let j = Number(key) + 1 ; j < Number(key) + k ; ++j ){
if(map[ j ] === undefined || x > map[ j ]){
return false
}
map[j] = map[j] - x
}
}
return true
}

How to understand returning values from recursive function calls?

I am trying to recursively solve a maze using Javascript, how do I return my solution from my recursive function call?
I am attempting to create a maze solver algorithm using recursion, in Javascript. My maze shall follow the following pattern:
let rawMaze =
[
[0, 1, 3],
[0, 1, 0],
[2, 1, 0]
],
Where
0: wall
1: valid path
2: start
3: end
I create an object from the source array,
let maze = []
constructMaze() {
for (let i = 0; i < 3; i++) {
maze[i] = [];
for (let j = 0; j < 3; j++) {
const Cell = {
x: j,
y: i,
state: rawMaze[i][j],
id: uniqueId()
};
this.maze[i].push(Cell);
}
}
console.table(this.maze);
}
I also use a helper function to get the neighbours of any given cell,
getNeighbours(x, y) {
let maze = this.maze;
let neighbours = [];
maze.forEach(row => {
row.forEach(cell => {
if (
(cell.x == x && cell.y == y + 1) ||
(cell.x == x && cell.y == y - 1) ||
(cell.y == y && cell.x == x + 1) ||
(cell.y == y && cell.x == x - 1)
) {
neighbours.push(cell);
}
});
});
return neighbours;
}
The main logic happens in my checkNeighbours function, where I determine the next possible moves and follow them up,
checkNeighbours(neighbours, path, visited) {
let validMoves = [];
neighbours.forEach(potentialMove => {
if (visited.indexOf(potentialMove.id) < 0) {
if (potentialMove.state !== 0) {
validMoves.push(potentialMove);
}
}
});
if (validMoves.length === 0) {
return;
} else {
let finish = validMoves.filter(cell => cell.state === 3);
console.log(finish);
if (finish.length === 1) {
return path;
}
}
validMoves.forEach(validMove => {
path.push(validMove);
visited.push(validMove.id);
this.checkNeighbours(
this.getNeighbours(validMove.x, validMove.y),
path,
visited
);
});
}
I then proceed to try and put this all together and solve the maze,
initSolve(maze) {
let maze = maze;
let start = [];
let paths = [];
let visited = [];
let current = null;
maze.forEach(row => {
row.forEach(cell => {
// Is start?
if ((start.length == 0) & (cell.state == 2)) {
start.push(cell);
visited.push(cell.id);
current = cell;
}
});
});
let result = this.checkNeighbours(
this.getNeighbours(current.x, current.y),
paths,
visited
);
console.log("test", result);
}
My question is the following. Using this very contrived and simple maze configuration, I have stepped through the code and can confirm that my
checkNeighbours()
function will recursively arrive at the end. At that point, the function has an array (the variable path) that contains the correct steps through the maze. How do I return this branch, if you will, from the recursive call? What happens when there are multiple branches?
The only thing I can think of is using a global variable, but I feel this can not be correct.
This is ripped from a React frontend , here is runnable code:
let rawMaze = [
[0, 1, 3],
[0, 1, 0],
[2, 1, 0]
]
let maze = []
function constructMaze() {
let counter = 0
for (let i = 0; i < 3; i++) {
maze[i] = [];
for (let j = 0; j < 3; j++) {
const Cell = {
x: j,
y: i,
state: rawMaze[i][j],
id: counter
};
maze[i].push(Cell);
counter++
}
}
}
function getNeighbours(x, y) {
let maze = this.maze;
let neighbours = [];
maze.forEach(row => {
row.forEach(cell => {
if (
(cell.x == x && cell.y == y + 1) ||
(cell.x == x && cell.y == y - 1) ||
(cell.y == y && cell.x == x + 1) ||
(cell.y == y && cell.x == x - 1)
) {
neighbours.push(cell);
}
});
});
return neighbours;
}
function checkNeighbours(neighbours, path, visited) {
let validMoves = [];
neighbours.forEach(potentialMove => {
if (visited.indexOf(potentialMove.id) < 0) {
if (potentialMove.state !== 0) {
validMoves.push(potentialMove);
}
}
});
if (validMoves.length === 0) {
return;
} else {
let finish = validMoves.filter(cell => cell.state === 3);
console.log(finish);
if (finish.length === 1) {
return path;
}
}
validMoves.forEach(validMove => {
path.push(validMove);
visited.push(validMove.id);
this.checkNeighbours(
this.getNeighbours(validMove.x, validMove.y),
path,
visited
);
});
}
function initSolve() {
let maze = constructMaze()
let start = [];
let paths = [];
let visited = [];
let current = null;
maze.forEach(row => {
row.forEach(cell => {
// Is start?
if ((start.length == 0) & (cell.state == 2)) {
start.push(cell);
visited.push(cell.id);
current = cell;
}
});
});
let result = this.checkNeighbours(
this.getNeighbours(current.x, current.y),
paths,
visited
);
console.log("test", result);
}
Might I recommend adding another class:
function Path() {
this.isValidPath = false;
this.pathArray = [];
}
And also reworking the checkNeighbours function to rename/include these parameters?
checkNeighbours(neighbours, paths, currentPathIndex, visited)
This way, paths could contain an array of Path classes, and you could set the isValidPath flag to true when you found a valid path (assuming you want to also include invalid and valid paths in the array). This would allow you to return all paths (branches). Each branch would be in the paths array at position currentPathIndex, which you'd increment in the code once one path is complete and you want to start searching for another path.
Also, currently the checkNeighbours function appears to do a breadth first search for valid moves. Perhaps if you reworked it into more of a depth-first traversal, then you could add each valid path (and exclude any invalid paths) to the paths array you return.

how to get mode in array

I have wounder`d for a while about how to get mode in array. That elements that are the same in array would be put together.
For ex. [Alex, Steven, Georg, Alice, Alex, Georg];
return would be: Alex: 2, Steven: 1, Georg: 2, Alice:1;
I wrote the code but it works only for numbers from 1 to 10. And for sure there is a better way.
(I don`t think you need my code but will paste it anyway.)
var mode = function (data){
var result1 = data.filter(function (verde) {return verde === 1});
var result2 = data.filter(function (verde) {return verde === 2});
var result3 = data.filter(function (verde) {return verde === 3});
var result4 = data.filter(function (verde) {return verde === 4});
var result5 = data.filter(function (verde) {return verde === 5});
var result6 = data.filter(function (verde) {return verde === 6});
var result7 = data.filter(function (verde) {return verde === 7});
var result8 = data.filter(function (verde) {return verde === 8});
var result9 = data.filter(function (verde) {return verde === 9});
var nyadata = [result1.length, result2.length,
result3.length, result4.length,
result5.length, result6.length,
result7.length, result8.length,
result9.length];
var nyarreymax = Math.max.apply(Math, nyadata);
if (nyarreymax === result1.length){return 1;}
if (nyarreymax === result2.length){return 2;}
if (nyarreymax === result3.length){return 3;}
if (nyarreymax === result4.length){return 4;}
if (nyarreymax === result5.length){return 5;}
if (nyarreymax === result6.length){return 6;}
if (nyarreymax === result7.length){return 7;}
if (nyarreymax === result8.length){return 8;}
if (nyarreymax === result9.length){return 9;}
else { return false;}
Hope you can help me to know code that works generally for strings and all integers.
I'm a beginner at js myself and was looking for this same solution not long ago. Here's one I found that should be what you're looking for:
function findMode(arr) {
var map = {};
for (var i = 0; i < arr.length; i++) {
if (map[arr[i]] === undefined) {
map[arr[i]] = 0;
}
map[arr[i]] += 1;
}
var greatestFreq = 0;
var mode;
for (var prop in map) {
if (map[prop] > greatestFreq) {
greatestFreq = map[prop];
mode = prop;
}
}
return mode;
}
You can try this using reduce() , see your console that shows value with counts.
Demo http://jsfiddle.net/ak69f/
var array_elements = ['Alex', 'Steven', 'Georg', 'Alice', 'Alex', 'Georg'];
var result = array_elements.reduce(function(p, c){
if (c in p) {
p[c]++;
} else {
p[c]=1;
}
return p;
}, []);
console.log(result);
Here's a simple recursive solution, which seems to be the fastest of the four answers as you can see here: http://jsperf.com/array-mode.
var a = ["Alex", "Steven", "Georg", "Alice", "Alex", "Georg"];
function getMode(a, result) {
result = result || {};
if (a.length === 0){
return result;
}
var head = a.shift();
if (result[head]){
result[head]++;
}
else{
result[head] = 1;
}
return getMode(a, result);
}
console.log(getMode(a));
First, define a new array that will hold your results.
Iterate through your names array. Inside of each loop, iterate through the results array. If the current name in your names array exists within the results array, change the value.
For example, if your names array is on the second "Alex", and you iterate through the results array and find that "Alex:1" already exists, change the value to "Alex:2" (you will have to do a little bit of string parsing for that).
If the name does not exist already, add it to the end as ":1"
Then if you want to return the mode, you will have to write another loop that finds the maximum occurrence. Have a variable that keeps track of the array position of the name with the highest number (let's say it's called maxIndex). For each item in the array, compare it to the value of the array at maxIndex. If it's higher, reset maxIndex to the current index. If it's equal to or less than, move onto the next item of the array.
I know that was very wordy, so let me know if you have any questions.
An alternative approach to this is to create a function that takes in your array, assigns each unique array value to an object property and if it already exists, increase the object properties value by one, like so;
function countArray(array){
var results = {};
for(var x = 0; x < array.length; x++){
if(results[array[x]] == undefined){
results[array[x]] = 1;
}else{
results[array[x]] += 1;
}
}
return results;
}
var checkArray = countArray(['alex', 'george', 'steve', 'alex']);
console.log(checkArray);
// outputs "Object {alex: 2, george: 1, steve: 1}"
Then you could access the results as needed by calling
console.log(checkArray.alex); // outputs 2
var numbers = [1,2,2,3,4,5];
var counts = numbers.reduce((counts, e) => { counts[e] = counts[e] ? counts[e] + 1 : 1; return counts; }, {});
var mode = Object.keys(counts).reduce((a, b) => (counts[a] > counts[b] ? a : b ));
console.log(mode);
Reduce function can help a lot in aggregations.
The way I have found is very similar to the accepted answer however I thought I would add that if no items repeat, or no single item repeats the most, there is no mode, so my function checks that and returns null if that's the case.
function calcMode(data) {
let counts = {};
data.forEach((d) => {
if (counts[d] === undefined) {
counts[d] = 0;
}
counts[d] += 1;
});
let mode,
max = 0,
repeats = 0;
Object.keys(counts).forEach((k) => {
if (counts[k] > max) {
max = counts[k];
mode = k;
repeats = 0;
} else if (counts[k] == max) repeats += 1;
});
if (!repeats) {
if (isNaN(mode)) return mode;
else return +mode;
} else return null;
}
This was my approach, I tried a "functional" style using reduce. It also supports multimodes, so it will return an array of modes.
export function mode(vector) {
if (vector.length === 0) return undefined
return (vector.reduce((accu, curr) => {
const freqsMap = accu.freqsMap
freqsMap.set(curr, (freqsMap.get(curr) || 0) + 1)
const maxCount = freqsMap.get(curr) > accu.maxCount
? freqsMap.get(curr)
: accu.maxCount
const modes = freqsMap.get(curr) === accu.maxCount
? [...accu.modes, curr]
: freqsMap.get(curr) > accu.maxCount
? [curr]
: accu.modes
return { freqsMap, maxCount, modes }
}, { freqsMap: new Map(), maxCount: 1, modes: []})).modes
}

Find next and previous keys in js array

I have an array
var arr = ["1", "3", "2", "4"];
I need a function that returns the next or the previous array key based on a given key value:
function closestTo(arr, key, direction) {
// do stuff here and return the next or previous id
}
So to find next of 4, I call the function; closestTo(arr, 4, 'next' ) this should return 1
And closestTo(arr, 4, 'prev' ) should return 2
Any ideas of this could also achieved with underscore?
Maybe something like this?
function closestTo(arr, key, direction) {
var offset_index = (direction === 'prev') ? -1 : 1;
// Convert to integers
var intarr = arr.map(function(x) {
return parseInt(x, 10);
});
return intarr[(intarr.length + intarr.indexOf(key) + offset_index) % intarr.length];
}
I have wrote script for you:)
http://jsfiddle.net/maxim_mazurok/6s7z6zwt/
But array should be like var arr = [1, 2, 3, 4]; if you want to call function with number as a second param.
var arr = [1, 2, 3, 4];
function closestTo(arr, key, direction) {
var last = arr.length - 1;
var first = 0;
var keyIndex = arr.indexOf(key);
switch (direction) {
case ('next'):
if (keyIndex != last) {
return arr[keyIndex + 1];
} else {
return arr[first];
}
break;
case ('prev'):
if (keyIndex != first) {
return arr[keyIndex - 1];
} else {
return arr[last];
}
}
}
alert(closestTo(arr, 4, 'next' ));
alert(closestTo(arr, 4, 'prev' ));
You only need pure JavaScript for this:
function closestTo(arr, key, direction) {
var keyIndex = arr.indexOf(key),
indexToReturn;
if (direction === 'prev') {
indexToReturn = keyIndex > 0 ? keyIndex - 1 : arr.length -1;
} else if (direction === 'next') {
indexToReturn = keyIndex < arr.length - 1 ? keyIndex + 1 : 0;
}
return arr[indexToReturn];
}

Javascript implementation of the inversion-counting with merge-sort algorithm

i am trying to implement the inversion-counting using merge sort algorithm in javascript. I found description and pseudo-code on this site.
My implementation looks like this:
var mergeAndCount, sortAndCount;
/*
the merging routine
#param List1 the first list to be merged
#param List2 the second list to be merged
*/
mergeAndCount = function(List1, List2) {
var count, outputList;
outputList = [];
count = 0;
while (List1.length > 0 || List2.length > 0) {
outputList.push(Math.min(List1[0], List2[0]));
if (List2[0] < List1[0]) {
count += List1.length;
List2.shift();
} else {
List1.shift();
}
}
outputList = outputList.concat(List1.concat(List2));
return {
'count': count,
'list': outputList
};
};
/*
count inversion algorithm
#param List the sequence to be sorted
*/
sortAndCount = function(List) {
var List1, List2, mergeOut, output1, output2;
if (List.length < 2) {
return {
'count': 0,
'list': List
};
} else {
List1 = List.splice(0, List.length / 2);
List2 = List;
output1 = sortAndCount(List1);
output2 = sortAndCount(List2);
mergeOut = mergeAndCount(List1, List2);
return {
'count': output1.count + output2.count + mergeOut.count,
'list': mergeOut.list
};
}
};
I wanted to test it on Jsfiddle here, but it crashes (too much memory used). Somehow it works for the inupt [1, 3, 2] but not for other. I am not sure what is going wrong, if my implementation or the original pseudocode is false.
Error 1 : infinite loop
The while goes on for a very long time when it starts to compare numbers with undefined. If List1.length is 0, the comparison List2[0] < List1[0] will always be false, resulting in List1.shift() which changes nothing.
Replace:
while (List1.length > 0 || List2.length > 0) {
With:
while (List1.length > 0 && List2.length > 0) {
Error 2 : manipulating arrays
You alter the arrays and then use what you expect to be their initial values. At the begining of each function you should copy the arrays (using slice is the fastest way).
Error 3 : ignoring output of sortAndCount
Replace:
mergeOut = mergeAndCount(List1, List2);
With:
mergeOut = mergeAndCount(output1.list, output2.list);
Correct solution:
var mergeAndCount, sortAndCount;
/*
the merging routine
#param List1 the first list to be merged
#param List2 the second list to be merged
*/
mergeAndCount = function(List1, List2) {
List1 = List1.slice();
List2 = List2.slice();
var count = 0;
var outputList = [];
while (List1.length > 0 && List2.length > 0) {
outputList.push(Math.min(List1[0], List2[0]));
if (List2[0] < List1[0]) {
count += List1.length;
List2.shift();
} else {
List1.shift();
}
}
outputList = outputList.concat(List1.concat(List2));
return {
'count': count,
'list': outputList
};
};
/*
count inversion algorithm
#param List the sequence to be sorted
*/
sortAndCount = function(List) {
List = List.slice();
var List1, List2, mergeOut, output1, output2;
if (List.length < 2) {
return {
'count': 0,
'list': List
};
} else {
List1 = List.splice(0, Math.floor(List.length / 2));
List2 = List;
output1 = sortAndCount(List1);
output2 = sortAndCount(List2);
mergeOut = mergeAndCount(output1.list, output2.list);
return {
'count': output1.count + output2.count + mergeOut.count,
'list': mergeOut.list
};
}
};
console.clear();
var r = sortAndCount([1,3,4,2]);
console.log('RESULT',r.list);
DEMO: http://jsbin.com/UgUYocu/2/edit
As pointed out, the problem was || instead of &&. Here's an implementation that seems to work (to make things interesting, it returns a list of inversions instead of simply counting them):
sort_and_count = function(L) {
if (L.length < 2)
return [[], L];
var m = L.length >> 1;
var na = sort_and_count(L.slice(0, m));
var nb = sort_and_count(L.slice(m));
var nc = merge_and_count(na[1], nb[1]);
return [[].concat(na[0], nb[0], nc[0]), nc[1]];
}
merge_and_count = function(a, b) {
var inv = [], c = [];
while(a.length && b.length) {
if(b[0] < a[0]) {
a.forEach(function(x) { inv.push([x, b[0]])});
c.push(b.shift());
} else {
c.push(a.shift());
}
}
return [inv, c.concat(a, b)];
}
nn = sort_and_count([2, 4, 1, 3, 5])
// [[[2,1],[4,1],[4,3]],[1,2,3,4,5]]
For completeness, here's the quadratic algorithm:
inversions = function(L) {
return L.reduce(function(lst, a, n, self) {
return self.slice(n).filter(function(b) {
return b < a;
}).map(function(b) {
return [a, b];
}).concat(lst);
}, []);
}
inversions([2, 4, 1, 3, 5])
// [[4,1],[4,3],[2,1]]

Categories

Resources