Let's take, for example, this array
ar = [6,3,5,1,2]
I want to convert it to another array and I might use only two operations - insert item at specific position (splice(i,0,item)) or remove item from specific position (splice(i,1)). I'm looking for the solution that uses minimal quantity of theese splices.
The second important condition is that we consider arrays with unique values, our arrays don't contain doubles.
For example,
ar1 = [6,3,10,5,1,2];
ar2 = [6,3,1,2,5];
That's obvious that if we want to get ar1 from ar, we need only one splice - ar.splice(2,0,10). If we want to get ar2, we have to do two splices: ar.splice(2,1) and then push(5) (the second equals to splice(ar.length,0,5))
By the way, this task has natural practical value. Let's imagine, for example, list of products and product filter. We change filter's settings and the list changes respectively. And every change followed by beauty slow jquery slide up - slide down animation. This animation might slide up and hide specific item or insert and slide down a new one. The task is to minify the quantity of theese animations. That means we try to minify the quantity of DOM-manipulations of the list.
The number of operations is exactly the edit distance (if you disallow substitution). Look up levenshtein distance.
You can modify the algorithm to calculate levenshtein distance to actually output the operations required.
I've wrote the code hopefully solving the problem. This code is somehow based on Levenshtein distance concept. It seems very useful for this problem, as was mentioned in maniek's answer.
For simplicity I've worked with strings instead the arrays and used Python.
It seems that original problem easily reduce to the same problem for two arrays of equal length consisting of the same set of integers. So, I assumed that the initial string and target string have the same length and consist of the same set of characters.
Python code:
import random
# Create random initial (strin) and target (strout) strings
s = "abcdefghijklmnopqrstuvwxyz"
l = list(s)
random.shuffle(l)
strout = ''.join(l)
random.shuffle(l)
strin = ''.join(l)
# Use it for tests
#strin = "63125798"
#strout = "63512897"
print strin, strout
ins_del = 0
for i in xrange(len(strin)-1, -1, -1):
if strin[i] != strout[i]:
if strin[i-1] == strout[i]:
ii = strout.find(strin[i], 0, i)
strin = strin[:ii] + strin[i] + strin[ii:i] + strin[i+1:]
ins_del = ins_del + 1
#Test output
print "1:", strin
else:
ii = strin.find(strout[i], 0, i-1)
strin = strin[:ii] + strin[ii+1:i+1] + strout[i] + strin[i+1:]
ins_del = ins_del + 1
#Test output
print "2:", strin
print strin, strout
# Check the result
for i in xrange(0, len(strin)):
if strin[i] != strout[i]:
print "error in", i, "-th symbol"
print "Insert/Delite operations = ", ins_del
Example of output:
kewlciprdhfmovgyjbtazqusxn qjockmigphbuaztelwvfrsdnxy
2: kewlciprdhfmovgjbtazqusxny
1: kewlciprdhfmovgjbtazqusnxy
2: kewlciprhfmovgjbtazqusdnxy
2: kewlciphfmovgjbtazqursdnxy
2: kewlciphmovgjbtazqufrsdnxy
2: kewlciphmogjbtazquvfrsdnxy
2: kelciphmogjbtazquwvfrsdnxy
2: keciphmogjbtazqulwvfrsdnxy
2: kciphmogjbtazquelwvfrsdnxy
2: kciphmogjbazqutelwvfrsdnxy
2: kciphmogjbaquztelwvfrsdnxy
2: kciphmogjbquaztelwvfrsdnxy
1: qkciphmogjbuaztelwvfrsdnxy
2: qkcipmogjhbuaztelwvfrsdnxy
2: qkcimogjphbuaztelwvfrsdnxy
1: qjkcimogphbuaztelwvfrsdnxy
2: qjkcmoigphbuaztelwvfrsdnxy
1: qjokcmigphbuaztelwvfrsdnxy
1: qjockmigphbuaztelwvfrsdnxy
qjockmigphbuaztelwvfrsdnxy qjockmigphbuaztelwvfrsdnxy
Insert/Delite operations = 19
Related
I have some tasks to handle in my daily jobs, so I need to do it in a automatic way. My task is:
there will be some messages sent to my IM, and I need to append the first, second & third number to each links with a "|".
if there only 2 numbers in the number line, a 0 is needed in the first place.
For example, in the cleanResult example, I need it to be done like:
finalResult = ["https://www.example.com/firstlink|500",
"https://www.example.com/firstlink|150",
"https://www.example.com/firstlink|30",
"https://www.exmaple.com/secondlink|600",
"https://www.exmaple.com/secondlink|150",
"https://www.exmaple.com/secondlink|30",
"https://www.example.com/thirdlink|500",
"https://www.example.com/thirdlink|150",
"https://www.example.com/thirdlink|30",
"https://www.example.com/forthlink|600",
"https://www.example.com/forthlink|100",
"https://www.example.com/forthlink|20",
"https://www.example.com/fithlink|0",
"https://www.example.com/fithlink|200",
"https://www.example.com/fithlink|50"
]
Here's the codes I had done so far:
const urlRegex = /(https?\:\/\/)?([^\.\s]+)?[^\.\s]+\.[^\s]+/gi;
const digitRegex = /^(?=.*\d)[\d ]+$/;
cleanResult = ["https://www.example.com/firstlink",
"https://www.exmaple.com/secondlink",
"https://www.example.com/thirdlink",
"500 150 30",
"https://www.example.com/forthlink",
"600 100 20",
"https://www.example.com/fithlink",
"200 50"
]
cleanResult.forEach((item, index) => {
if (item.match(digitRegex)) {
//codes I don't know how to do...
}
})
Are elements in cleanResult always either a URL or a number? In that case, you could just check the first character of the string to see if it's a number (basically a non-url). If it's not a URL, then we know it's numbers, and we can do something with the URL, which should the the previous element:
// If it's a URL, we will store it here for future use
let currentURL = ''
cleanResult.forEach((item, index) => {
// Get the first character of this string
const first = item[0]
if (!Number.isInteger(first)) {
// This is NOT a number, so must be a URL,
// let's store it in our variable to use in the next loop
currentURL = item
} else {
// This IS a number, which means we need to do a few things:
// 1. Split into separate numbers
// 2. Create a new URL pattern from each number
// 3. Push to finalResult
// 1. Split by tab delimiter (?)
// splits by tab, and returns an array
const numbers = item.split('\t')
// 2. Create a new URL pattern from each number
numbers.forEach((n) {
// This should now give you the URL + | + the number:
// ex: https://example.com/firstlink|500
const newURL = currentURL + '|' + n
// 3. push to the finalResult array
finalResult.push(newURL)
})
}
})
I haven't tested it, but this is the process that I generally use: break it into smaller tasks and take it one step at a time. I also didn't use regex, just to make it easier. We're assuming that you will receive either a URL or a list of numbers separated by a tab. This means you can afford to keep it a bit simple.
I'm sure there are way more efficient ways to do it and in a lot fewer lines, but if you're just learning JS or programming, there is nothing wrong with being extra verbose so that you can understand early concepts.
I have a scenario like Need to edit the single quotes values (only single quotes values),
So I extracted the single quotes values using regex and prepare the reactive dynamic form.
onclick of performing edit button will show old step name above, new step name below, submit step will replace the step name in the original array.
WOrking fine as expected in few scenarios according to my approach, but in scenarios, I realized whatever algorithm I am following does not fulfill my requirement.
Below are the test cases
Test case 1:
Step Name: "Then I should hire an employee using profile '1' for 'USA'",
// Here --> '1', 'USA' values are editable
Test case 2: "And Employee should be hired on '01' day of pay period '01' of 'Current' Fiscal"
// '01', '01', 'Current'
Issues: in test case 2 if I tried to edit second 01 it is editing the first 01
I try to solve the perform edit function with help of indexof, substring functions
this.replaceString = this.selectedStep.name;
this.metaArray.forEach((element: any) => {
var metaIndex = this.replaceString.indexOf(element.paramValue);
if (metaIndex !== -1) {
const replaceValue = this.stepEditForm.controls[element['paramIndex']].value;
this.replaceString = this.replaceString.substring(0, metaIndex) + replaceValue + this.replaceString.substring(metaIndex + (element.paramValue.length));
}
});
but in indexof always find the first occurrence of a value in a string. So I realized my approach is wrong on performed it function
please find the attachment for the code
https://stackblitz.com/edit/angular-reactive-forms-cqb9hy?file=app%2Fapp.component.ts
So Can anyone please suggest to me how to solve this issue,
Thanks in advance
I added a function called matchStartingPositions that returns the starting position indexes of each match. Using this method you can then perform your edit by replacing the string just as you do, but we'll find the proper match to be replaced at the given position.
So in your line
var metaIndex = this.replaceString.indexOf(element.paramValue);
we can then add a second parameter to indexOf, that is the starting point:
var metaIndex = this.replaceString.indexOf(element.paramValue, startingPositions[element.paramIndex]);
The function for getting the index positions just looks for those single quotes in a given string:
matchStartingPositions(str) {
let count = 0;
let indices = [];
[...str].forEach((val, i) => {
if (val === "'") {
if (count % 2 == 0) {
indices.push(i);
}
count++;
}
});
return indices;
}
Here it is in action:
https://angular-reactive-forms-xhkhmx.stackblitz.io
https://stackblitz.com/edit/angular-reactive-forms-xhkhmx?file=app/app.component.ts
I´m stuck with a problem... given an input number I´m trying to output a string of length 4. The string has to be divided into 2 parameter sections "On"/"Off".
For example:
-If the input number is 16, then the string should get combined as follows:
"On" section = "On" * Math.floor(16/5) = 3 --> "On On On".
"Off" section should be: length-On-section = 4-3 = 1 --> "Off".
Hence the string should look like "On On On Off".
I´m currently trying to narrow my solution to a nicer approach than using a for loop. I have to repeat this process various times in my function to create strings following the same approach but in various lengths and "On"/"Off" section ratios. but I´m not sure how to set it up properly..
this is one example:
function hoursTop(hour) {
var lights = [], on = Math.floor(hour/5), off = 4 - topLightsOn;
for(var i=1; i<=on; i++){
lights.push('On');
}
for(var j=1; j<=Off; j++){
lights.push('Off');
}
return lights.join("");
}
This produces way too much code overall.. Thanks for helping me out!
You can use String#repeat to create the strings, then concat them, and trim the extra spaces:
function hoursTop(hour) {
var on = Math.floor(hour/5), off = 4 - on;
return 'on '.repeat(on) + 'off '.repeat(off).trim();
}
console.log(hoursTop(16));
Or you can use Array#fill to create the array, then concat them, and join the array to a string:
function hoursTop(hour) {
var on = Math.floor(hour/5), off = 4 - on;
return Array(on).fill('on').concat(Array(off).fill('off')).join(' ');
}
console.log(hoursTop(16));
I'm not sure how to word the question and i'm still quite new at javascript.
So I've got a random quote generator that has each quote result as an array. I'd like to add in two items in the array which I've got so far but having one result be a random number generated eg "2 quote" but having 2 be randomised each time. The end result is for a browser based text game. So it could be "2 zombies attack" or "7 zombies attack." The code I have so far is:
var quotes = [
[x, 'Zombies attack!'],
[x, 'other creatures attack'],
['next line'],
]
function newQuote() {
var randomNumber = Math.floor(Math.random() * (quotes.length));
document.getElementById('quote').innerHTML = quotes[randomNumber];
}
Ideally need x(or i however it's going to work) to be the result of a random number between a set range, each differently each array.
Thank you
p.s I forgot to mention that not all the quotes require a number. Thats why I've done it as a double array.
If I understand your goal correctly, you want to have a set of similar-ish message templates, pick one of them at some point and fill it with data, correct? There's a lot of ways to tackle this problem, depending on how varying can your templates be. For a simple case in my head where you just need to prepend a number to a string I'd do something like this:
var messages = [" zombies attack",
" other creatures attack"], // define your messages
messageIndex = Math.floor(Math.random() * messages.length), // pick one of them
numberOfMonsters = Math.floor(Math.random() * 10 + 1), // get your random number
result = numberOfMonsters + messages[messageIndex]; // construct a resulting message
document.getElementById('quote').textContent = result;
If you'd rather have more complex strings where you don't necessarily add a number (or any string) to the beginning, like ["There's X things in the distance", "X things are somewhere close"], then I'd recommend to either come up with some sort of string formatting of your own or use a library to do that for you. sprintf.js seems to be just right for that, it will let you do things like this:
var messages = ["%d zombies attack",
"A boss with %d minions attacks"], // define your messages
messageIndex = Math.floor(Math.random() * messages.length), // pick one of them
numberOfMonsters = Math.floor(Math.random() * 10 + 1), // get your random number
result = sprintf(messages[messageIndex], numberOfMonsters) // format a final message
document.getElementById('quote').textContent = result;
EDIT: Your task is much more complex than what is described in the original question. You need to think about you code and data organization. You have to outline what is finite and can be enumerated (types of actions are finite: you can loot, fight, move, etc.), and what is arbitrary and dynamic (list of monsters and loot table are arbitrary, you have no idea what type and amount of monsters game designers will come up with). After you've defined your structure you can come up with some quick and dirty message composer, which takes arbitrary entities and puts them into finite amount of contexts, or something. Again, I'm sort of shooting in the dark here, but here's an updated version of the code on plunkr.
I solved it to do what I want and still have the numbers different. The issue was I should have had the number generator within the quote function. Also can create multiple variables to use too for different number generators. The plan is to then integrate it with php to add content dynamically. Which I can do. Thanks Dmitry for guiding me in the right direction.
function newQuote() {
var MonsterOne = Math.floor((Math.random() * 14) + 0);
var MonsterTwo = Math.floor((Math.random() * 14) + 0);
var MonsterThree = Math.floor((Math.random() * 14) + 0);
var MonsterFour = Math.floor((Math.random() * 14) + 0);
var quotes = [
['Test', MonsterOne, 'One'],
['Test', MonsterOne,'Two'],
['Test', MonsterThree, 'Three'],
[MonsterFour, 'Four'],
['Five'],
]
var randomNumber = Math.floor(Math.random() * (quotes.length));
document.getElementById('quote').innerHTML = quotes[randomNumber];
}
I am trying to develop the addition program using column addition in javascript, For e.g: 53,22 , we add numbers from the right 3+2 and 5+2 finally results in 75, the main problem is with large numbers i am trying to develop a program which can implement addition of large numbers.so that i don't get gibberish like 1.26E+9, when adding large numbers. i tried doing it by defining the code like below
function add(a,b)
{
return (Number(a) + Number(b)).toString();
}
console.log(add('58685486858601586', '8695758685'));
i am trying to get the added number without getting the gibberish like 5.8685496e+16
You can add them digit by digit.
function sumStrings(a, b) { // sum for any length
function carry(value, index) { // cash & carry
if (!value) { // no value no fun
return; // leave shop
}
this[index] = (this[index] || 0) + value; // add value
if (this[index] > 9) { // carry necessary?
carry.bind(this)(this[index] / 10 | 0, index + 1); // better know this & go on
this[index] %= 10; // remind me later
}
}
var array1 = a.split('').map(Number).reverse(), // split stuff and reverse
array2 = b.split('').map(Number).reverse(); // here as well
array1.forEach(carry, array2); // loop baby, shop every item
return array2.reverse().join(''); // return right ordered sum
}
document.write(sumStrings('58685486858601586', '8695758685') + '<br>');
document.write(sumStrings('999', '9') + '<br>');
document.write(sumStrings('9', '999') + '<br>');
document.write(sumStrings('1', '9999999999999999999999999999999999999999999999999999') + '<br>');
I would keep all values as numbers until done with all the calculations. When ready to display just format the numbers in any way you want. For example you could use toLocaleString.
There are several libraries for that
A good rule of thumb is to make sure you do research for libraries before you actually go ahead and create you're own proprietary implementation of it. Found three different libraries that all solve your issue
bignumber.js
decimal.js
big.js
Example
This is how to use all three of the libraries, BigNumber coming from the bignumber.js library, Decimal from decimal.js and Big from big.js
var bn1 = new BigNumber('58685486858601586');
var bn2 = new BigNumber('8695758685');
console.log(bn1.plus(bn2).toString());
bn1 = new Decimal('58685486858601586');
bn2 = new Decimal('8695758685');
console.log(bn1.plus(bn2).toString());
bn1 = new Big('58685486858601586');
bn2 = new Big('8695758685');
console.log(bn1.plus(bn2).toString());
The console's output is :
58685495554360271
58685495554360271
58685495554360271