Related
I would like to get find elements having same characters but in different order in an array. I made javascript below,is there any way to create Javascript function more basic? Can you give me an idea? Thank you in advance..
<p id="demo"></p>
<script>
const arr1 = ["tap", "pat", "apt", "cih", "hac", "ach"];
var sameChars = 0;
var subArr1 = [];
for(var i = 0; i < arr1.length; i++){
for(var j = i+1; j < arr1.length; j++){
if(!subArr1.includes(arr1[i]) && !subArr1.includes(sortAlphabets(arr1[i]))){
subArr1.push(arr1[i]);
sameChars++;
}
if(sortAlphabets(arr1[i]) == sortAlphabets(arr1[j])){
if(!subArr1.includes(arr1[j])){
subArr1.push(arr1[j]);
}
}
}
}
function sortAlphabets(text1) {
return text1.split('').sort().join('');
};
document.getElementById("demo").innerHTML = sameChars;
</script>
I would just use reduce. Loop over split the string, sort it, join it back. Use it as a key in an object with an array and push the items onto it.
const arr1 = ["tap", "pat", "apt", "cih", "hac", "ach"];
const results = arr1.reduce((obj, str) => {
const key = str.split('').sort().join('');
obj[key] = obj[key] || [];
obj[key].push(str);
return obj;
}, {});
console.log(Object.values(results));
You can get the max frequency value by building a map and getting the max value of the values.
const frequencyMap = (data, keyFn) =>
data.reduce(
(acc, val) =>
(key => acc.set(key, (acc.get(key) ?? 0) + 1))
(keyFn(val)),
new Map());
const groupMap = (data, keyFn) =>
data.reduce(
(acc, val) =>
(key => acc.set(key, [...(acc.get(key) ?? []), val]))
(keyFn(val)),
new Map());
const
data = ["tap", "pat", "apt", "cih", "hac", "ach"],
sorted = (text) => text.split('').sort().join(''),
freq = frequencyMap(data, sorted),
max = Math.max(...freq.values()),
groups = groupMap(data, sorted);
document.getElementById('demo').textContent = max;
console.log(Object.fromEntries(freq.entries()));
console.log(Object.fromEntries(groups.entries()));
.as-console-wrapper { top: 2em; max-height: 100% !important; }
<div id="demo"></div>
Maybe split the code into two functions - one to do the sorting and return a new array, and another to take that array and return an object with totals.
const arr = ['tap', 'pat', 'apt', 'cih', 'hac', 'ach'];
// `sorter` takes an array of strings
// splits each string into an array, sorts it
// and then returns the joined string
function sorter(arr) {
return arr.map(str => {
return [...str].sort().join('');
});
}
// `checker` declares an object and
// loops over the array that `sorter` returned
// creating keys from the strings if they don't already
// exist on the object, and then incrementing their value
function checker(arr) {
const obj = {};
for (const str of arr) {
// All this line says is if the key
// already exists, keep it, and add 1 to the value
// otherwise initialise it with 0, and then add 1
obj[str] = (obj[str] || 0) + 1;
}
return obj;
}
// Call `checker` with the array from `sorter`
console.log(checker(sorter(arr)));
<p id="demo"></p>
Additional documentation
map
Loops and iteration
Spread syntax
here is the task: I am trying to build an anagram finder which receives a given string (simple word) from an input, then check in a dictionary (of 350k words) and then return all the anagrams of that word. Here is where I got so far:
const button = document.getElementById("findButton");
button.addEventListener("click", function() {
let typedText = document.getElementById("input").value;
let wordIn = typedText.toLowerCase().split("").sort().join("").trim();
let output = [];
for (let i = 0; i < dictionary.length; i++) {
if (dictionary[i].length === wordIn.length)
output.push(dictionary[i])
}
let sorted = [];
let result = [];
for (let i = 0; i < output.length; i++) {
sorted.push(output[i].toLowerCase().split("").sort().join("").trim())
if (wordIn === sorted[i]) {
result.push(sorted[i])
}
}
});
With the current approach I get as final output an array with words alphabeticaly sorted i.e.: cat ("act", "act", "act"...) , but I need them as they are in the dictionary. I was thinking if I could store the index of the words in the output array (which already reduces from 350k to only those with same length) and after temporary sort them by their alphabetic value, I could get only those which match and return them as a new array. i.e cat = "cat", "act", "tac".
You can use filter:
const
// Once, at the top of your script:
normalizeWord = w => w.toLowerCase().split("").sort().join("").trim(),
dictionary = ['foo', 'act', 'bar', 'cat', 'example', 'word'],
sortedDict = dictionary.map(normalizeWord),
// In your event handler, or its own named function:
wordIn = normalizeWord('tac'),
result = dictionary.filter((_, i) => sortedDict[i] === wordIn);
console.log(result);
You can first of all increase your performance by getting the desired result in the first loop itself, thus no requirement of extra array and extra loop saving space and time.
const button = document.getElementById("findButton");
button.addEventListener("click", function() {
let typedText = document.getElementById("input").value;
let wordIn = typedText.toLowerCase().split("").sort().join("").trim();
let output = []; //it will have your final anagram list
for (let i = 0; i < dictionary.length; i++) {
let sortedWord = dictionary[i].toLowerCase().split("").sort().join("").trim();
if (sortedWord === wordIn) {
output.push(dictionary[i]);
}
}
});
Instead of result.push(sorted[i])
You should result.push(output[i]) instead.
I'm a bit curious why the behaviors are different between the two solutions posted below. In the Failing solution, I've concated the zeros array to what I assume by the time of execution, would be the result array of the filter operation. I'm curious why the result is not the updated concated variant (an array with 0s at the end) and instead if simply the initial output of the filter operation.
Passing:
const moveZeros = function (arr) {
let zeros = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) zeros.push(0);
}
let filteredArray = arr.filter( element => element !== 0).concat(zeros)
return filteredArray;
//returns [1,2,3,0,0,0]
}
Failing:
const moveZeros = function (arr) {
let zeros = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) zeros.push(0);
}
let filteredArray = arr.filter( element => element !== 0);
// shouldnt the line below concat zeros to the filter result?
filteredArray.concat(zeros);
return filteredArray;
//returns [1,2,3]
}
This also passes:
return filteredArray.concat(zeros)
concat() doesn't modify the array in place, it returns a new array. Your failing version doesn't return the new array, it returns the original array.
Array.concat() return the concatenated array instead of the original.
let arr = [1,0,0,2,3,0];
let zeros = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) zeros.push(0);
}
let filteredArray = arr.filter( element => element !== 0);
console.log(filteredArray);
let concatenatedArray = filteredArray.concat(zeros);
console.log(concatenatedArray);
So you need to reassign filteredArray like:
filteredArray = filteredArray.concat(zeros);
From the MDN Web Docs
The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
In your passing solution you are assigning the result of a .concat() to a variable, and then returning that variable, whereas in your failing solution, you are returning the original array, because you did not assign the result of filteredArray.concat(zeros) to anything
How to write a function to remove certain elements into a new array and leave the original array with only the remaining elements?
the first part is easy using a for loop pushing the even numbers into a new array but mutating the original array to leave only the odd numbers is hard
function remove(arr, cb){
var removed = [];
var newArr = [];
for(var i = 0; i < arr.length; i++) {
if(cb(arr[i], i, arr)) {
removed.push(arr[i]);
}
}
return removed;
}
Use an else statement to fill newArr with values that should stay in the original arr, then empty it using splice() before copying the items from newArr back into it.
function remove (arr, cb) {
var removed = [];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (cb(arr[i], i, arr)) {
removed.push(arr[i]);
} else {
newArr.push(arr[i]);
}
}
arr.splice(0);
for (var i = 0; i < newArr.length; i++) {
arr.push(newArr[i]);
}
return removed;
}
Welcome to Stackoverflow!
Personally, I'd avoid anything that mutates an input parameter, as this increases code complexity and makes it hard to reason about what's happening from the calling side.
Instead, I'd write a method that returns an array of two arrays. This can be easily split into two variables at the calling end using by using array destructuring.
See the example below:
const splitArr = (arr, pred) =>
arr.reduce(
(prev, curr, idx) => {
prev[+pred(curr, idx, arr)].push(curr);
return prev;
}, [[], []]
);
// usage //
const myArr = [1, 2, 3, 4];
const [arr1, arr2] = splitArr(myArr, x => x > 2);
console.log(arr1);
console.log(arr2);
Because pred is a function that returns a boolean value, we can co-erce this value to 0 or 1 using +someBoolean. We can then use this value as an index to decide into which of the two output arrays the value should be pushed.
You were definitely on the right track with your solution, a couple tweaks and we can make it very readable and also very easy to work with. I tried to keep the format of what it looked like you were doing.
I do take advantage of destructuring here, this could be returned as just an object, and then reference the properties.
const myArr = [0,1,2,3,4,5,6,7,8,9,10];
const splitItems = (arr, logicFunc) => {
let secondSet = []
const firstSet = arr.filter(v => {
if (logicFunc(v)) return true
else secondSet.push(v)
})
return { firstSet, secondSet }
}
const myLogicFunc = v => (v < 3 || v == 9)
const { firstSet, secondSet } = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${firstSet}`) // My first set: 0,1,2,9
console.log(`My second set: ${secondSet}`) // My second set: 3,4,5,6,7,8,10
/* OR without destructuring:
const myArrays = splitItems(myArr, myLogicFunc)
console.log(`My first set: ${myArrays.firstSet}`)
console.log(`My second set: ${myArrays.secondSet}`)
*/
Please let me know if you have any questions
In modern JavaScript apps we do not mutate arrays we create new array, this avoids side effects, so what we do is create two new arrays
const split = (source, conditionFunc) = [ source.filter(i => conditionFunc(i)), source.filter(i => !conditionFunc(i))];
Then you have an array of two arrays of the values that meed condition and those that don't and you have not caused any side effects.
const odssAndEvens = split(source, i => i % 2 === 1);
Or with reduce so you don't iterate the array twice
const split = (source, conditionFunc) = source.reduce((results, item) => {
if (conditionFunc(item)) {
results[0].push(item);
} else {
results[1].push(item);
}
return results;
}, [[],[]]);
I have two arrays, keys and commonkeys.
I want to create a key-value pair using these two arrays and the output should be like langKeys.
How to do that?
This is array one:
var keys=['en_US','es_ES', 'pt_PT','fr_FR','de_DE','ja_JP','it_IT']
This is array two:
var commonKeys=['en-*','es-*', 'pt-*','fr-*','de-*','ja-*','it-*', '*']
This is the output I need:
var langKeys = {
'en-*': 'en_US',
'es-*': 'es_ES',
'pt-*': 'pt_PT',
'fr-*': 'fr_FR',
'de-*': 'de_DE',
'ja-*': 'ja_JP',
'it-*': 'it_IT',
'*': 'en_US'
};
You can use map() function on one array and create your objects
var keys=['en_US','es_ES', 'pt_PT','fr_FR','de_DE','ja_JP','it_IT'];
var commonKeys=['en-*','es-*', 'pt-*','fr-*','de-*','ja-*','it-*', '*'];
var output = keys.map(function(obj,index){
var myobj = {};
myobj[commonKeys[index]] = obj;
return myobj;
});
console.log(output);
JavaScript is a very versatile language, so it is possible to do what you want in a number of ways. You could use a basic loop to iterate through the arrays, like this:
var keys=['en_US','es_ES', 'pt_PT','fr_FR','de_DE','ja_JP','it_IT']
var commonKeys=['en-*','es-*', 'pt-*','fr-*','de-*','ja-*','it-*', '*']
var i;
var currentKey;
var currentVal;
var result = {}
for (i = 0; i < keys.length; i++) {
currentKey = commonKeys[i];
currentVal = keys[i];
result[currentKey] = currentVal;
}
This example will work in all browsers.
ES6 update:
let commonKeys = ['en-*', 'es-*', 'pt-*', 'fr-*', 'de-*', 'ja-*', 'it-*', '*'];
let keys = ['en_US', 'es_ES', 'pt_PT', 'fr_FR', 'de_DE', 'ja_JP', 'it_IT', 'en_US'];
let zipArrays = (keysArray, valuesArray) => Object.fromEntries(keysArray.map((value, index) => [value, valuesArray[index]]));
let langKeys = zipArrays(commonKeys, keys);
console.log(langKeys);
// let langKeys = Object.fromEntries(commonKeys.map((val, ind) => [val, keys[ind]]));
What you want to achieve is to create an object from two arrays. The first array contains the values and the second array contains the properties names of the object.
As in javascript you can create new properties with variales, e.g.
objectName[expression] = value; // x = "age"; person[x] = 18,
you can simply do this:
var keys=['en_US','es_ES', 'pt_PT','fr_FR','de_DE','ja_JP','it_IT'];
var commonKeys=['en-*','es-*', 'pt-*','fr-*','de-*','ja-*','it-*', '*'];
var langKeys = {};
var i;
for (i=0; i < keys.length; i++) {
langKeys[commonKeys[i]] = keys[i];
}
EDIT
This will work only if both arrays have the same size (actually if keys is smaller or same size than commonKeys).
For the last element of langKeys in your example, you will have to add it manually after the loop.
What you wanted to achieve was maybe something more complicated, but then there is missing information in your question.
Try this may be it helps.
var langKeys = {};
var keys=['en_US','es_ES', 'pt_PT','fr_FR','de_DE','ja_JP','it_IT']
var commonKeys=['en-*','es-*', 'pt-*','fr-*','de-*','ja-*','it-*', '*']
function createArray(element, index, array) {
langKeys[element]= keys[index];
if(!keys[index]){
langKeys[element]= keys[index-(commonKeys.length-1)];
}
}
commonKeys.forEach(createArray);
console.info(langKeys);
Use a for loop to iterate through both of the arrays, and assign one to the other using array[i] where i is a variable representing the index position of the value.
var keys = ['en_US', 'es_ES', 'pt_PT', 'fr_FR', 'de_DE', 'ja_JP', 'it_IT'];
var commonKeys = ['en-*', 'es-*', 'pt-*', 'fr-*', 'de-*', 'ja-*', 'it-*', '*'];
var langKeys = {};
for (var i = 0; i < keys.length; i++) {
var commonkey = commonKeys[i];
langKeys[commonkey] = keys[i];
}
console.log(JSON.stringify(langKeys));
let keys = ['en_US', 'es_ES', 'pt_PT', 'fr_FR', 'de_DE', 'ja_JP', 'it_IT'];
let commonKeys = ['en-*', 'es-*', 'pt-*', 'fr-*', 'de-*', 'ja-*', 'it-*', '*'];
// declaration of empty object where we'll store the key:value
let result = {};
// iteration over first array to pick up the index number
for (let i in keys) {
// for educational purposes, showing the number stored in i (index)
console.log(`index number: ${i}`);
// filling the object with every element indicated by the index
// objects works in the basis of key:value so first position of the index(i)
// will be filled with the first position of the first array (keys) and the second array (commonKeys) and so on.
result[keys[i]] = commonKeys[i];
// keep in mind that for in will iterate through the whole array length
}
console.log(result);