Replace instances of an array in a string with another array - javascript

I'm working on a JS applet that requires replacement of array entries in a given string with entires from another array. This is my code as it now stands:
const string = "Lion, Unicorn, Unicorn";
const array1 = ["Lion", "Unicorn"];
const array2 = ["Fox", "Hound"];
const string2 = string.replaceAll(array1[0],array2[0]) //returns "Fox, Unicorn, Unicorn"
My desired output is :
Fox, Hound, Hound.
Specifically, I'd like to turn the contents of string2 into a function that repeats for every item in an array, but have no idea where to start.
Thanks!

Do you mean something like this?
I hope I understood the question well.
You can write a recursive function:
let string = "Lion, Unicorn, Unicorn";
let array1 = ["Lion", "Unicorn"];
let array2 = ["Fox", "Hound"];
function myCustomReplace(str, a1, a2) {
let wordToReplace=a1.shift(); // a1[0] - if array change matters
let replacementWord=a2.shift(); // a2[0] - if array change matters
if (!wordToReplace || !replacementWord) return str;
str=str.replaceAll(wordToReplace, replacementWord );
return myCustomReplace(str,a1,a2); // rturn myCustomReplace(str,a1.slice(1),a2.slice(1)) - if array change matters
}
console.log(
myCustomReplace(string,array1,array2)
)

It's sometimes worthwhile to first transform the inputs into a shape that is easier to work on. For this problem, the input sentence is better thought of as an array of words, and the two arrays used for replacement are better represented as a single object mapping input words to output words...
let string = "Lion, Unicorn, Unicorn";
let array1 = ["Lion", "Unicorn"];
let array2 = ["Fox", "Hound"];
// transform the inputs
let input = string.split(", ");
let translator = array1.reduce((acc, key, i) => {
acc[key] = array2[i];
return acc;
}, {});
// now input is ['Lion', 'Unicorn', ...]
// and transator is { 'Lion' : 'Fox', ... }
// now the problem is just a two-liner, mapping the input over the translator
let output = input.map(e => translator[e] || e)
console.log(output.join(", "))

If we use split(', ') to convert the string to an array of single words, we can use map() to replace them by searching for a pair with indexOf():
Please see comments in the code. A one-liner can be found at the end.
const string = "Lion, Unicorn, Unicorn";
const array1 = ["Lion", "Unicorn"];
const array2 = ["Fox", "Hound"];
// Split on ', '
let splitted = string.split(', ');
// Map
let result = splitted.map(w => {
// Get position in array1
const i = array1.indexOf(w);
// If we've found something
if (i !== -1) {
// Return replacement
return array2[i];
} else {
// Return original
return w;
}
});
// Create string
result = result.join(', ');
// Show
console.log(result);
// Or, as a one-liner
let result2 = string.split(', ').map(w => (array1.indexOf(w) !== -1) ? array2[array1.indexOf(w)] : w).join(', ');
console.log(result2);

Related

Processing the data into a desired format in javaScript

I am new to JavaScript and want to process the following array -
var a = [
"John-100",
"Mark-120",
"John-50",
"Mark-130"
]
into the following format -
a = {
"John": [100, 50],
"Mark": [120, 130]
}
But have been unable to do so. Any help will be very much appreciated.
Edit - Any other format ideas where the marks of a particular student can be grouped together are also welcome.
Here is one way to achieve what you described:
var a=[
"John-100",
"Mark-120",
"John-50",
"Mark-130"
]
function convertToSpecialObject(input) {
//setup the output as an empty object
const output = {};
// iterate through input array one element at a time
input.forEach(e => {
// split the current element by dividing it into part[0] before the dash
// and part[1] after the dash sign
const parts = e.split(/-/);
// now check the output object if it already contains a key for the part before the dash
if(!output[parts[0]]) {
// in this case, we don't have a key for it previously
// so lets set it up as a key with an empty array
output[parts[0]] = [];
}
// we must have already created a key or there is a key in existence
// so let's just push the part after the dash to the current key
output[parts[0]].push(Number(parts[1]));
});
// work done
return output;
}
const b = convertToSpecialObject(a);
console.log(b);
you can achieve this by using reduce and split method
var a=[
"John-100",
"Mark-120",
"John-50",
"Mark-130"
]
const b = a.reduce((acc, val) => {
const _split = val.split('-');
const name = _split[0]
if(acc && acc[name]) {
acc[name].push(+_split[1])
} else {
acc[name] = [+_split[1]]
}
return acc;
}, {});
console.log(b)
You can achieve it in a very simple way by just using a Array.forEach() method along with the String.split().
Live Demo :
var a = [
"John-100",
"Mark-120",
"John-50",
"Mark-130"
];
const obj = {};
a.forEach(element => {
if (!obj[element.split('-')[0]]) {
obj[element.split('-')[0]] = [];
}
obj[element.split('-')[0]].push(element.split('-')[1])
});
console.log(obj);
With Simple Approach
const input = [
"John-100",
"Mark-120",
"John-50",
"Mark-130"
];
const getCustomObject = (arr) => {
const obj = {};
for (let i = 0; i < arr.length; i++) {
const split = arr[i].split('-'); //spliting with '-'
if (obj[split[0]]) {
//push to existing array
obj[split[0]].push(split[1]);
} else {
obj[split[0]] = []; //initilize array if no member
obj[split[0]].push(split[1]);
}
};
return obj;
}
console.log(getCustomObject(input));
Now numbers are not numerical values, It can be achieved with parseInt or parseFloat
As I suggested, string split, and array reduce - add in an array map and it's a single line of code
let a=["John-100","Mark-120","John-50","Mark-130"];
a=a.map(v=>v.split('-')).reduce((r,[n,m])=>({...r,[n]:[...r[n]||[],+m]}),{});
console.log(JSON.stringify(a));
The only answer with the correct result ... an array of NUMBERS

A function that returns an array which is the intersection of two other arrays

function arraysCommon(array1, array2) {
return array1.filter(x => array2.includes(x));
}
This function does not work the way I want it to.
For instance given array1 = [1,2,3,2,1] and array2 = [5,4,3,2,1]
it returns [1,2,3,2,1], since the elements 1,2,3 are seen in both arrays.
But I want it to return [1,2,3] in that order since 1,2,3 are seen only once in array2 and are treated as seperate entities.
So pretty much the functionality should be that
Each element in the first array can map to at most one element in the second array.
Duplicated elements in each array are treated as separate entities.
the first array determines the order
I have attempted to loop through the arrays and check and compare the number of duplicates in each array but I can't seem to get the logic working correctly. Is there a different way to approach this?
I've attached an image of two Venn diagrams that might clarify the difference
Unfortunately, it gets more complicated because you need to know what numbers you have already added. In this case you need a temporary array to hold the result. We also need to track if a number exists in the array two times.
Try this:
function arraysCommon(array1, array2) {
//Copy array2 by duplicating and spreading the elements into another array.
var copyArray2 = [...array2];
//Temperary Array
var temp = [];
for (let x of array1) {
//Check if the element is in the first array and not already added to the temp array
if (copyArray2.includes(x)) {
temp.push(x);
//Remove item from copy array2 so it cannot be used anymore
copyArray2.splice(copyArray2.indexOf(x), 1);
}
}
//Return the temp array
return temp;
}
console.log(arraysCommon([1,2,3,2,1], [5,4,3,2,1]))
console.log(arraysCommon([1,2,3,2,1], [2,2,3,3,4]))
With sorting and counting this should be possible. Since you are incrementing when you find similar characters, this should be okay:
const array1= [1,4,1,1,5,9,2,7];
const array2 = [1,8,2,5,1]
const array3 = [1,2,3,2,1];
const array4 = [5,4,3,2,1,2]
const array5 = [1,2,3,2,1];
const array6 = [2,2,3,3,4]
function arraysCommon(array1, array2) {
const ans = [];
array1.sort();
array2.sort();
let j = 0;
let i = 0;
while(i<array1.length && j<array2.length){
if(array1[i] === array2[j]){
ans.push(array1[i]);
i++;
j++;
}
else if(array2[i] > array1[j]){
i++;
}
else{
j++;
}
}
console.log(ans);
}
arraysCommon(array1,array2);
arraysCommon(array3,array4);
arraysCommon(array5,array6);
this should work as you wanted!
// test 1
const array1 = [1,4,1,1,5,9,2,7];
const array2 = [1,8,2,5,1];
// test 2
const array3 = [1,2,3,2,1];
const array4 = [5,4,3,2,1];
const mapper = (array1, array2) => {
var obj = {};
array1.forEach((x, indexX) => {
array2.forEach((y, indexY) => {
if (x == y) {
if (!Object.values(obj).includes(indexX) && !obj.hasOwnProperty(indexY)) {
obj[indexY] = indexX;
return;
}
}
})
})
return Object.values(obj).sort().map(values => array1[values]);
}
console.log(mapper(array1, array2));
console.log(mapper(array3, array4));
I hope this helps. Cheers.
You can instance a new Set, wich brings only unique values and than retorn a array from this set.
Something like this:
function arraysCommon(array1, array2) {
const filtered = array1.filter(x => array2.includes(x));
const uniqueValues = new Set(filtered)
return Array.from(uniqueValues)
}

How can I find anagrams in an array of words?

I am trying to get all anagrams from a array of words:
arr = ['cab','bac','tru']
The expected output is supposed to be:
{abc: ['cab','bac']}
I tried to implement the below code:
var words = ['cab', 'bac', 'mihir']
let result = {}
words.forEach(word => {
var a = word.split("").sort().join("");
result[word] = a
})
console.log(result)
How do I iterate over the values so that I can access the keys if they have the same values?
You can use the sorted words as keys in an object, and collect each word that matches the key in an array:
var words = ['cab', 'bac', 'mihir']
let result = {}
for (const word of words) {
const sorted = word.split("").sort().join("");
if (sorted in result) {
// If there is already an entry in the result, append this word
result[sorted].push(word);
} else {
// Otherwise, create one
result[sorted] = [word];
}
}
console.log(result);

Sort array by custom function in JavaScript

I am using only Javascript.
I have the following string :
?pn1=age&pn2=name&pv1=12&pv2=alice
What I need to do, is have the following outcome :
age:12|name:alice
I thought of a way to do this, it is the following :
var str = "?pn1=age&pn2=name&pv1=12&pv2=alice";
var strSplit = str.split("&");
for (var i = 0; i < strSplit.length; i++) {
console.log(strSplit[i]);
}
This returns the following result :
?pn1=age
pn2=name
pv1=12
pv2=alice
Since I want to join together pn1 and pv1 and pn2 and pv2, the number present in the end of the string is important.
?pn1=age
pn2=name
pv1=12
pv2=alice
So I thought a way to do this is to sort the array by this number. and then joining every 2 values together after sorting.
I tried the following code :
strSplit.sort(function() {
var pref = strSplit[i].split('=')[0];
return pref.charAt(pref.length-1);
});
It does not seem to work
Any help would be appreciated
You could split the parts, collect all items and return a joined string.
var string = '?pn1=age&pn2=name&pv1=12&pv2=alice',
result = string
.slice(1)
.split('&')
.reduce((r, p) => {
var [k, value] = p.split('='),
[key, index] = k.split(/(\d+)/);
index--;
r[index] = r[index] || {};
r[index][key] = value;
return r;
}, [])
.map(({ pn, pv }) => [pn, pv].join(':'))
.join('|');
console.log(result);
You can do that in following steps.
You can loop through half of the array and add corresponding keys and values to an array.
Consider i is the current index when we loop through half array.
The element at position i will be key.
Add the half of the length and add it to i to get corresponding value.
split() both key and value by = and get the second element.
var str = "?pn1=age&pn2=name&pv1=12&pv2=alice";
var arr = str.split("&");
let half = arr.length/2
let res = [];
for (var i = 0; i < half; i++) {
res.push(`${arr[i].split('=')[1]}:${arr[i + half].split('=')[1]}`);
}
console.log(res.join('|'))
You could use URLSearchParams to convert the query string to a collection of key-value pair.
Then loop through them to group the the pv and pn values based on the number.
Separate the string and and number values using the regex: (\D+)(\d+)
Loop through the obj.pn and get the corresponding pv value for the same number
Join the resulting array with |
This works with pn and pv values in any random order
const searchParams = new URLSearchParams("?pn1=age&pn2=name&pv1=12&pv2=alice")
const obj = { pn: {}, pv: {} }
for (let [key, value] of searchParams) {
const [, k, number] = key.match(/(\D+)(\d+)/)
obj[k][number] = value
}
const output = Object.entries(obj.pn)
.map(([n, key]) => `${key}:${obj.pv[n]}`)
.join("|")
console.log(output)
One idea is to first split values on & and add it to take digit as key and place on object and then later place the respective values in desired format
var str = "?pn1=age&pn2=name&pv1=12&pv2=alice".replace(/^\?/,'')
var strSplit = str.split("&");
let op = strSplit.reduce((op,inp) => {
let [key,value] = inp.split('=')
let digit = key.match(/\d+/)[0]
op[digit] = op[digit] || []
op[digit].push(value)
return op
},{})
let final = Object.values(op).reduce((op,inp) => {
let [key,value] = inp
op.push(`${key}:${value}`)
return op
} ,[]).join(' | ')
console.log(final)
You could convert that & split to a string and remove the ?xxx= then split it again by , to finally have an array with the stuff you're looking at.
var str = "?pn1=age&pn2=name&pv1=12&pv2=alice";
var split = str.split('&').toString().replace(/([?]?[pnv0-9]+[=])/g,'').split(',');
console.log(split[0] + ':' + split[2] + '|' + split[1] + ':' + split[3]);
EDIT : worth mentioning for those who are looking to the best performing solution, I tested all those provided here, click here for more infos.
The solution's list from the fastest to the slowest :
Maheer Ali (113,610 ops/s ±1.48% fastest)
tcj (112,324 ops/s ±1.01% 1.13% slower)
Nina Scholz (81,067 ops/s ±1.81% 28.64% slower)
Code Maniac (80,074 ops/s ±0.99% 29.52% slower)
adiga (33,065 ops/s ±0.92% 70.9% slower)

JavaScript to split data and calculate sums

I believe what I need are two JavaScript functions. I am receiving a comma separated string that holds two types of data: 1) device name followed by 2) numeric value. These two values are separated by a comma, and each set is also separated by a comma. Example string below:
Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7
What I want to do is create two separate functions. The first function finds the unique device names and returns just the names in a comma separated string. The second function would calculate the sum of the numeric values for each device. The expected results from the example string above would return:
Function 1 (Device List):
Device_A, Device_B, Device_C
Function 2 (Sums per Device List):
15,10,9
The lists do not need to return in any particular order as long at they both match up. All I have successfully done at this point is return a list of unique values (including numeric values)... I'm stuck on separating the list, but still referring to device name to sum up all of the values.
Thanks in advance. Let me know if you have any questions!
Matt
You could use an object for collecting the names and count.
This edit contains a shared function and two function for the result in equal order.
function getGrouped(data) {
var array = data.split(','),
temp = Object.create(null),
i = 0;
while (i < array.length) {
temp[array[i]] = (temp[array[i]] || 0) + +array[i + 1] || 0;
i += 2;
}
return temp;
}
function getDevices(data) {
var temp = getGrouped(data);
return Object.keys(temp).sort().join();
}
function getCounts(data) {
var temp = getGrouped(data);
return Object.keys(temp).sort().map(function (k) { return temp[k]; }).join();
}
var data = "Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7";
console.log(getDevices(data));
console.log(getCounts(data));
When starting out on a problem like this, I think it's wise to not worry about doing it in a single loop or in a fancy one-liner at first.
A) Start out by defining what data structures you need and how to go from one format to another:
Convert my string of data to a list of keys and values
Somehow group these keys and values based on the key
Sum the values for each group
Return a list of all unique keys
Return a list of all summed values
B) Then, try to see if any of the code you've written has the potential be re-used by other parts of your application and refactor accordingly.
C) Finally, assess if there are performance bottle necks and only if there are, optimize for performance.
A. A function for each step:
// 1. From string to array of keys and values
// You already figured this one out. Split by ","!
const namesAndValuesFromString =
str => str.split(",");
// 2. Grouping by key
// Let's first make pairs:
const deviceValuePairs = devicesAndValues => {
let pair = [];
const pairs = [];
devicesAndValues.forEach(x => {
pair.push(x);
if (pair.length === 2) {
pairs.push(pair);
pair = [];
}
});
return pairs;
};
// Key value pairs are a nice starting point for constructing a grouped object:
const kvpsToDeviceValuesObj = kvps => {
const valuesByDevice = {};
kvps.forEach(([key, value]) => {
value = Number(value);
if (!valuesByDevice[key]) {
valuesByDevice[key] = [];
}
valuesByDevice[key].push(value);
});
return valuesByDevice;
};
// 3. Now, we can get to summing the values arrays
const sumValueArrays = valuesByDevice => {
const summedValuesByDevice = {};
// Loop over the objects entries
Object.entries(valuesByDevice).forEach(
([key, values]) => {
summedValuesByDevice[key] = values
.reduce((a, b) => a + b);
}
);
return summedValuesByDevice;
};
// 4. + 5. Now that we have an object with device ids as keys, and summed values inside, we can retrieve the two lists
const getDevices = Object.keys;
const getSums = Object.values;
// Running the code:
const namesAndValues =
namesAndValuesFromString("A,5,C,2,A,10,B,8,B,2,C,7");
console.log(namesAndValues);
const kvps = deviceValuePairs(namesAndValues);
console.log(kvps);
const valuesByDevice = kvpsToDeviceValuesObj(kvps);
console.log(valuesByDevice);
const sumValues = sumValueArrays(valuesByDevice);
console.log(sumValues);
const devices = getDevices(sumValues);
console.log(devices);
const sums = getSums(sumValues);
console.log(sums);
B. Refactoring!
Once you understand each of those steps, you'll start to see things that can be generalized or combined. That's where the fun starts :)
// UTILITIES
const split = del => arr => arr.split(del);
const toPairs = arr => {
let pair = [];
return arr.reduce(
(pairs, x) => {
pair.push(x);
if (pair.length === 2) {
pairs.push(pair);
pair = [];
}
return pairs;
}, []);
};
const sum = (x, y = 0) => +x + y;
const kvpsToGroups = grouper => kvps =>
kvps.reduce(
(groups, [key, value]) => Object.assign(groups, {
[key]: grouper(value, groups[key])
}), {});
// YOUR APP
const sumGrouper = kvpsToGroups(sum);
const dataSplitter = split(",");
const parseData = str => sumGrouper(toPairs(dataSplitter(str)));
// MAIN
const result = parseData("A,5,C,2,A,10,B,8,B,2,C,7");
console.log("devices:", Object.keys(result));
console.log("sums:", Object.values(result));
another way by regexs
let str = "Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7", obj = {}
str.match(/(\w+,[0-9]+)/g).forEach((s) => {
s = s.split(',')
obj[s[0]] = (obj[s[0]] || 0) + (Number(s[1]) || 0)
})
console.log(obj)
Something like this should do it:
var input = "Device_A,5,Device_C,2,Device_A,10,Device_B,8,Device_B,2,Device_C,7";
var output = input.split(',').reduce((accumulator, currentValue, currentIndex, array) => {
accumulator[currentValue] = (accumulator[currentValue] || 0)
+ parseInt(array[currentIndex + 1]);
array.splice(0,1);
return accumulator;
}, {});
console.log(Object.keys(output));
console.log(Object.keys(output).map(k => output[k]));

Categories