Related
I have the below array
["0,5,p1", "24,29,p2", "78,83,p2", "78,83,p3", "162,167,p2" ]
i want the output as ["5,p1","10,p2","5,p3"] , so p1..3 are video files paying time with start and end time . 0,5 mean p1 profile played for 5 sec and so on.
I want to know what profile take what time in total using ECMA script map,reduce function. Here is what i tried but it doesnt work:
var ca = uniqueArray.reduce(function(pval, elem) {
var spl = elem.split(',');
var difference = Math.round(spl[1] - spl[0]);
return difference;
},elem.split(',')[3]);
I dont think it can be done in one pass, but I could be wrong. I'd go for a 2 step...
Reduce the array to get unique map of pX values
Map the result back to an array in the required format
var input = ["0,5,p1", "24,29,p2", "78,83,p2", "78,83,p3", "162,167,p2" ]
var step1 = input.reduce(function(p,c){
var parts = c.split(",");
if(!p[parts[2]])
p[parts[2]] = 0;
p[parts[2]] += parseInt(parts[1],10) - parseInt(parts[0],10);
return p;
},{});
var result = Object.keys(step1).map(function(e){
return step1[e] + "," + e;
});
console.log(result);
You could use es6 map:
arrayWithNumbers.map(a => {var spl = a.split(','); return (spl[1] - spl[0]) + "," + spl[2]})
For a single loop approach, you could use a hash table for same third parts, like 'p1'. If a hash is given, then update the value with the actual delta.
var array = ["0,5,p1", "24,29,p2", "78,83,p2", "78,83,p3", "162,167,p2"],
hash = Object.create(null),
result = array.reduce(function(r, a) {
var parts = a.split(','),
delta = parts[1] - parts[0],
key = parts[2];
if (!(key in hash)) {
hash[key] = r.push([delta, key].join()) - 1;
return r;
}
r[hash[key]] = [+r[hash[key]].split(',')[0] + delta, key].join();
return r;
}, []);
console.log(result);
I have updated the code. Please check now.
var ca = ["0,5,p1", "24,29,p2", "78,83,p2", "78,83,p3", "162,167,p2" ] .reduce(function(result, elem) {
var spl = elem.split(',');
var difference = Math.round(spl[1] - spl[0]);
var found = false
for (var i = 0 ; i < result.length; i++) {
if (result[i].split(',')[1] == spl[2]) {
result[i] = parseInt(result[i].split(',')[0]) + difference+","+spl[2];
found = true;
}
}
if (!found) result.push(difference+","+spl[2]);
return result;
},[]);
console.log("modified array",ca);
I have an algorithm where the user will enter a string and I will parse it into an array of 2+ dimensions. So, for example, the user can enter 1,2,3;4,5,6 and set the text to be parsed by the semicolon and the comma. The first pass through will create an array with 2 entries. The second pass through will create a 3 entry array in both prior spots.
The user can add or remove the number of text items to be used to parse the original string such as the semicolon or comma, meaning the resulting array can have as many dimensions as parsing items.
This doesn't seem like a difficult problem, but I have run into some snags.
Here is my code so far.
vm.parsers = [';', ','];
vm.inputString = "1,2,3,4,5;6,7,8,9,10";
function parseDatasetText( )
{
vm.real = vm.parseMe( vm.inputString, 0);
};
function parseMe( itemToParse, indexToParse )
{
if ( indexToParse < vm.parsers.length )
{
console.log('Parsing *'+itemToParse+'* with '+vm.parsers[indexToParse]);
var tempResults = itemToParse.split( vm.parsers[indexToParse] );
for (var a=0; a<tempResults.length; a++)
{
console.log('Pushing '+tempResults[a]);
tempResults[a] = vm.parseMe( tempResults[a], parseInt( indexToParse ) + 1 )
console.log('This value is '+tempResults[a]);
}
}else
{
console.log('Returning '+itemToParse);
return itemToParse
}
};
As you can see from the console logs, the algorithm spits out an undefined after the last parse, and the final answer is undefined.
Maybe I just haven't slept enough, but I was thinking that the array would recursively populate via the splits?
Thanks
function parseDatasetText(){
//composing parser from right to left into a single function
//that applies them from left to right on the data
var fn = vm.parsers.reduceRight(
(nextFn, delimiter) => v => String(v).split(delimiter).map(nextFn),
v => v
);
return fn( vm.inputString );
}
Don't know what else to add.
You can use a simple recursive function like the following (here an example with 3 different delimiters):
function multiSplit(xs, delimiters) {
if (!delimiters.length) return xs;
return xs.split(delimiters[0]).map(x => multiSplit(x, delimiters.slice(1)));
}
data = '1:10,2:20,3:30;4:40,5:50,6:60';
res = multiSplit(data, [';', ',', ':']);
console.log(res)
The following function should suit your requirements, please let me know if not
var parsers = [';', ',', ':'],
inputString = "1:a,2:b,3:c,4:d,5:e;6:f,7:g,8:h,9:i,10:j",
Result = [];
function Split(incoming) {
var temp = null;
for (var i = 0; i < parsers.length; i++)
if (incoming.indexOf(parsers[i]) >= 0) {
temp = incoming.split(parsers[i]);
break;
}
if (temp == null) return incoming;
var outgoing = [];
for (var i = 0; i < temp.length; i++)
outgoing[outgoing.length] = Split(temp[i])
return outgoing;
}
Result = Split(inputString);
try it on https://jsfiddle.net/cgy7nre1/
Edit 1 -
Added another inputString and another set of parsers: https://jsfiddle.net/cgy7nre1/1/
Did you mean this?
var inputString = "1,2,3,4,5;6,7,8,9,10";
var array=inputString.split(';');
for (var i=0;i<array.length;i++){
array[i]=array[i].split(',');
}
console.log(array);
I have an array which looks like this:
["1,8", "4,6,8", "8,9", "6,9"]
1/ I would like to turn it in to this
[1,8,4,6,8,8,9,6,9]
2/ I would then like to find matching values, by looking for the most number:
[8]
This first has been solved with this:
var carArray = ["1,8", "4,6,8,7,7,7,7", "8,9", "6,9"];
//1) create single array
var arr = carArray.join().split(',');
//2) find most occurring
var counts = {}; //object to hold count for each occurence
var max = 0, maxOccurring;
arr.forEach(function(el){
var cnt = (counts[el] || 0); //previous count
counts[el] = ++cnt;
if(cnt > max && cnt > 1){ //only register if more than once (cnt>1)
max=cnt;
maxOccurring = el;
}
});
if(maxOccurring){
//there was an element more than once, maxOccuring contains that element
setResult('Most occuring: ' + maxOccurring + ' (' + max + ' times)');
}
else{
//3)/4) ???
setResult('sorting?');
}
//below is only for test display purposes
function setResult(res){
console.log(res);
}
3/ If the are no matching values like this
[1,8,4,6,5,7]
4/ Then I need to compare this array to another array, such as this
[6,7,4,1,2,8,9,5]
If the first number in <4> array above appears in <3> array, then get that number, ie in the above example I need to get 6. The <4> array will be static values and not change. The numbers is <3> will be dynamic.
EDIT Not the most elegant of answers, but I do have something working now. I didn't compare the original array directly with the second array, instead used simple if/else statements to do what I needed:
var carArray = ["1,5", "4", "8,2", "3,9,1,1,1"];
//1) create single array
var arr = carArray.join().split(',');
//2) find most occurring
var counts = {}; //object to hold count for each occurence
var max = 0, maxOccurring;
arr.forEach(function(el){
var cnt = (counts[el] || 0); //previous count
counts[el] = ++cnt;
if(cnt > max && cnt > 1){ //only register if more than once (cnt>1)
max=cnt;
maxOccurring = el;
}
});
if(maxOccurring){
//there was an element more than once, maxOccuring contains that element
console.log('Most occuring: ' + maxOccurring + ' (' + max + ' times)');
console.log(maxOccurring);
}
else {
// If not occuring, match from a list
if(jQuery.inArray("6", arr) !== -1) { console.log('6'); }
else if(jQuery.inArray("9", arr) !== -1) { console.log('9'); }
else if(jQuery.inArray("7", arr) !== -1) { console.log('7'); }
else if(jQuery.inArray("5", arr) !== -1) { console.log('5'); }
else if(jQuery.inArray("4", arr) !== -1) { console.log('4'); }
else if(jQuery.inArray("1", arr) !== -1) { console.log('1'); }
else { console.log('not found'); }
}
Example Fiddle
Step 1 is fairly easy by using javascript's join and split methods respectively:
var arr = carArray .join().split(',');
For step 2, several methods can be used, the most common one using an object and using the elements themselves as properties. Since you only need to get the most occurring value if there is a reoccurring value, it can be used in the same loop:
var counts = {}; //object to hold count for each occurence
var max = 0, maxOccurring;
arr.forEach(function(el){
var cnt = (counts[el] || 0); //previous count
counts[el] = ++cnt;
if(cnt > max && cnt > 1){ //only register if more than once (cnt>1)
max=cnt;
maxOccurring = el;
}
});
After the above, the variable maxOccurring will contain the reoccurring value (if any) and max will contain the times it occured
For step 4 the easiest way is to loop through the compare array and get the element that occurs in the input array:
var cmpArr = ['6','7','4','1','2','8','9','5'];
//find the first occurrence inside the cmpArr
res = function(){ for(var i= 0 ; i < cmpArr.length; i++){ if(arr.indexOf(cmpArr[i]) !== -1)return cmpArr[i];}}();
The above uses an in place function which is called immediately to be able to use return. You could also just use a loop and assign res when found, then break from the loop.
Last update, an alternate fiddle where the above is converted to a single function: http://jsfiddle.net/v9hhsdny/5/
Well first of all the following code results in four matching answers since the jQuery selectors are the same.
var questionAnswer1 = $(this).find('input[name=questionText]').val();
var questionAnswer2 = $(this).find('input[name=questionText]').val();
var questionAnswer3 = $(this).find('input[name=questionText]').val();
var questionAnswer4 = $(this).find('input[name=questionText]').val();
var carArray = [questionAnswer1, questionAnswer2, questionAnswer3, questionAnswer4];
You could use the eq(index) method of jQuery to select the appropriate element. However having 4 inputs with the same name is a bad practice.
Well lets say that the carArray has 4 different values which all consist out of comma separated numbers. You could then do the following:
var newArr = [];
carArray.forEach(function(e) {
e.split(",").forEach(function(n) {
newArr.push(n);
});
});
Well then we got to find the most occurring number. JavaScript doesn't have any functions for that so we will have to find an algorithm for that. I found the following algorithm on this stackoverflow page
var count = function(ary, classifier) {
return ary.reduce(function(counter, item) {
var p = (classifier || String)(item);
counter[p] = counter.hasOwnProperty(p) ? counter[p] + 1 : 1;
return counter;
}, {})
}
var occurances = count(newArr);
It isn't clear to me what you're trying to do in step 3 and 4, so can't answer those at the moment.
var ary = ["1,8", "4,6,8", "8,9", "6,9"];
var splitted = ary.reduce(function(acc, item) {
return acc.concat(item.split(','));
}, []);
var occurences = splitted.reduce(function(acc, item) {
if (!acc.hasOwnProperty(item)) acc[item] = 0;
acc[item] += 1;
return acc;
},{}),
biggest = Object.keys(occurences).reduce(function (acc, key) {
if (occurences[key] > acc.occurences) {
acc.name = key;
acc.occurences = occurences[key];
}
return acc;
},{'name':'none','occurences':0}).name;
var vals=["1,8", "4,6,8", "8,9", "6,9"];
// 1) turn into number array
var arrNew=[];
for(var i=0; i<vals.length; i++)
{
arrLine=vals[i].split(",");
for (var j=0;j<arrLine.length;j++) { arrNew.push (parseInt(arrLine[j])) }
}
//result:
alert(arrNew.join(";");
// 2) find most common
var found=[];
for(var i=0; i<arrNew.length; i++) {
// make an array of the number of occurrances of each value
if (found["num"+newArray[i]]) {
found["num"+newArray[i]] ++ ;
} else {
found["num"+newArray[i]]=1;
}
}
var mostCommon={count:0,val:"ROGUE"};
for (x in found) {
if (found[x] > mostCommon.count) {
mostCommon.count=found[x].count;
mostCommon.val=x;
}
}
// result :
alert(mostCommon.val);
//3) not quite sure what you meant there
// 4) unique values:
// at this point the 'found' list contains unique vals
var arrUnique=[];
for (x in found) {
arrUnique.push[x];
}
// result :
alert(arrUnique.join(";"))
//sort:
arrUnique.sort(function(a, b){return a-b});
(This won't work in most browsers) but on a side note, when ES6 becomes widely supported, your solution could look like this:
var arr1 = ["1,8", "4,6,8", "8,9", "6,9"];
var arr2 = arr1.join().split(',');
var s = Array.from(new Set(arr2)); //Array populated by unique values, ["1", "8", "4", "6", "9"]
Thought you might like to see a glimpse of the future!
1.
var orgArray = ['1,8', '4,6,8', '8,9', '6,9'];
var newArray = [];
for (var i in orgArray) {
var tmpArray = orgArray[i].split(',');
for (var j in tmpArray) {
newArray.push(Number(tmpArray[j]));
}
}
2.
var counts = {};
var most = null;
for (var i in newArray) {
var num = newArray[i];
if (typeof counts[num] === 'undefined') {
counts[num] = 1;
} else {
++(counts[num]);
}
if (most == null || counts[num] > counts[most]) {
most = num;
} else if (most != null && counts[num] === counts[most]) {
most = null;
}
}
I don't understand the question 3 and 4 (what "unique order" means) so I can't answer those questions.
i found the following solution to add sorting capabilities to jQuery (ref: jQuery sort()). I've altered the solution to sort strings of unknown length. It works nicely, however as you might notice from the function name: it sorts acscending :-).
jQuery.fn.sort = function() {
return this.pushStack( [].sort.apply( this, arguments ), []);
};
function sortStringAscending(a,b){
var h1 = a.innerHTML;
var h2 = b.innerHTML;
var c = 0;
var str1 = null,
var str2 = null;
while(str1 == str2 && (c < h1.length && c < h2.length)) {
str1 = h1.substring(c,c+1).toLowerCase().charCodeAt(0);
str2 = h2.substring(c,c+1).toLowerCase().charCodeAt(0);
if(str1 > str2) {
r = 1;
} else if(str2 > str1) {
r = -1;
}
c += 1;
}
return r;
};
The function is used like this:
$('ol li').sort(sortStringAscending).appendTo('ol');
Is it possible to alter this code in such a way that the following will become possible?
$('ol li').sort(sortString, 0).appendTo('ol'); //0 for descending
$('ol li').sort(sortString, 1).appendTo('ol'); //1 for ascending
You are not going to be able to easily get an additional argument into the array sort function that the jQuery.fn.sort uses.
It will be easier to use two separate functions, one for ascending, and one for descending, but keep the actual comparision in a third function.
function sortStringAscending(a,b) {
return sortString(a,b,1);
};
function sortStringDescending(a,b) {
return sortString(a,b,-1);
};
function sortString(a,b,direction) {
var h1 = a.innerHTML.toLowerCase();
var h2 = b.innerHTML.toLowerCase();
if(h1 > h2) {
r = 1*direction;
} else if(h2 > h1) {
r = -1*direction;
}
return r;
};
Also note that you can simply compare two strings, there is no need to compare them character by character, unless you want to have unpredictable sort order for items like "abc" and "abcd".
Then you should be able to do
$('ol li').sort(sortStringAscending).appendTo('ol');
$('ol li').sort(sortStringDescending).appendTo('ol');
This will do what you've asked: (use a parameter to decide sort order)
function sortString(a,b,direction){
var d = (direction?1:-1);
var h1 = a.html();
var h2 = b.html();
var c = 0;
var str1 = null,
var str2 = null;
while(str1 == str2 && (c < h1.length && c < h2.length)) {
str1 = h1.substring(c,c+1).toLowerCase().charCodeAt(0);
str2 = h2.substring(c,c+1).toLowerCase().charCodeAt(0);
if(str1 > str2) {
r = 1*d;
} else if(str2 > str1) {
r = -1*d;
}
c += 1;
}
return r;
};
Changing this:
if(str1 > str2) {
r = 1;
} else if(str2 > str1) {
r = -1;
}
to this:
if(str1 > str2) {
r = -1;
} else if(str2 > str1) {
r = 1;
}
will swap the sort order.
EDIT:
Because of the way Javascript's array.sort(callback) function work it is not easy to just add a third parameter for the callback to accept, I will suggest using 2 separate functions mySortAsc and mySortDesc which in turn could be calling a third function and passing whatever parameters are needed.
You might be able to use a closure. Although the javascript array sort method expects a 2 parameter function, you can bring other information into the callback function's scope by doing something like:
function BiSort(direction)
{
return function( a, b )
{
var d = ( direction ? 1 : -1 ) ;
return ( a > b ? 1 : b > a ? -1 : 0 ) * d ;
}
}
The BiSort function returns a new function that has access to the outer function's parameter list. Now you can do this:
var a = ["ant", "goat", "bat", "zebra", "yak"] ;
a.sort(BiSort(true)); // sorts ascending
a.sort(BiSort(false)); // sort descending
I know this is old, but for future reference, I would make the 2 functions for asc/desc, then make a var point to one or the other based on what you need and then pass that var to the array.sort()
function SortAsc(a, b){
}
function SortDesc(a, b){
}
var sortOrder
// if asc button clicked
sortOrder = SortAsc;
// if desc button clicked
sortOrder = SortDesc;
$('ol li').sort(sortOrder);
I have a string that looks something like the following 'test:1;hello:five;just:23'. With this string I need to be able to do the following.
....
var test = MergeTokens('test:1;hello:five;just:23', 'yes:23;test:567');
...
The end result should be 'test:567;hello:five;just:23;yes:23' (note the exact order of the tokens is not that important).
Just wondering if anyone has any smart ideas of how to go about this. I was thinking a regex replace on each of the tokens on right and if a replace didn't occur because there was not match just append it. But maybe there is better way.
Cheers
Anthony
Edit: The right side should override the left. The left being what was originally there and the right side being the new content. Another way of looking at it, is that you only keep the tokens on the left if they don't exist on the right and you keep all the tokens on the right.
#Ferdinand
Thanks for the reply. The problem is the efficiency with which the solution you proposed. I was initially thinking down similar lines but discounted it due to the O(n*z) complexity of the merge (where n and z is the number tokens on the left and right respectively) let alone the splitting and joining.
Hence why I was trying to look down the path of a regex. Maybe behind the scenes, regex is just as bad or worse, but having a regex which removes any token from the left string that exists on the right (O(n) for the total amount of token on the right) and then just add the 2 string together (i.e. vat test = test1 + test2) seems more efficient. thanks
I would use join() and split() to create some utility functions to pack and unpack your token data to an object:
// Unpacks a token string into an object.
function splitTokens(str) {
var data = {}, pairs = str.split(';');
for (var i = 0; i < pairs.length; ++i) {
var pair = pairs[i].split(':');
data[pair[0]] = pair[1];
}
return data;
}
// Packs an object into a token string.
function joinTokens(data) {
var pairs = [];
for (var key in data) {
pairs.push(key + ":" + data[key]);
}
return pairs.join(';');
}
Using these, merging is easy:
// Merges all token strings (supports a variable number of arguments).
function mergeTokens() {
var data = {};
for (var i = 0; i < arguments.length; ++i) {
var d = splitTokens(arguments[i]);
for (var key in d) {
data[key] = d[key];
}
}
return joinTokens(data);
}
The utility functions are also useful if you want to extract some keys (say,"test") and/or check for existence:
var data = splitTokens(str);
if (data["test"] === undefined) {
// Does not exist
} else {
alert("Value of 'test': " + data["test"]);
}
The following is what I ended thiking about. What do you guys recon?
Thanks
Anthony
function Tokenizer(input, tokenSpacer, tokenValueSpacer) {
this.Tokenizer = {};
this.TokenSpacer = tokenSpacer;
this.TokenValueSpacer = tokenValueSpacer;
if (input) {
var TokenizerParts = input.split(this.TokenSpacer);
var i, nv;
for (i = 0; i < TokenizerParts.length; i++) {
nv = TokenizerParts[i].split(this.TokenValueSpacer);
this.Tokenizer[nv[0]] = nv[1];
}
}
}
Tokenizer.prototype.add = function(name, value) {
if (arguments.length == 1 && arguments[0].constructor == Object) {
this.addMany(arguments[0]);
return;
}
this.Tokenizer[name] = value;
}
Tokenizer.prototype.addMany = function(newValues) {
for (nv in newValues) {
this.Tokenizer[nv] = newValues[nv];
}
}
Tokenizer.prototype.remove = function(name) {
if (arguments.length == 1 && arguments[0].constructor == Array) {
this.removeMany(arguments[0]);
return;
}
delete this.Tokenizer[name];
}
Tokenizer.prototype.removeMany = function(deleteNames) {
var i;
for (i = 0; i < deleteNames.length; i++) {
delete this.Tokenizer[deleteNames[i]];
}
}
Tokenizer.prototype.MergeTokenizers = function(newTokenizer) {
this.addMany(newTokenizer.Tokenizer);
}
Tokenizer.prototype.getTokenString = function() {
var nv, q = [];
for (nv in this.Tokenizer) {
q[q.length] = nv + this.TokenValueSpacer + this.Tokenizer[nv];
}
return q.join(this.TokenSpacer);
}
Tokenizer.prototype.toString = Tokenizer.prototype.getTokenString;
i am a few years late, but i think this is what you are looking for:
function MergeTokens(input, replace){
var replaceTokens = replace.split(";");
for(i=0; i<replaceTokens.length; i++){
var pair = replaceTokens[i].split(":");
var result = input;
regString = "\\b" + pair[0] + ":[\\w]*\\b";
var reg = new RegExp(regString);
if(reg.test(result)){
result = result.replace(reg, replaceTokens[i]);
}
else{
result = result + replaceTokens[i];
}
}
return result;
}