How to modify my code to get all non-unique values? - javascript

With duplicates, this code worked fine
const findDuplicates = (word) => {
let arr = word.toLowerCase();
let sorted_arr = [...arr].slice().sort();
console.log(sorted_arr);
let results = [];
for (let i = 0; i < sorted_arr.length - 1; i++) {
if (sorted_arr[i + 1] == sorted_arr[i]) {
results.push(sorted_arr[i]);
}
}
return results;
}
console.log(findDuplicates('piccdda123dd'));
Output
[
'1', '2', '3', 'a',
'c', 'c', 'd', 'd',
'd', 'd', 'i', 'p'
]
[ 'c', 'd', 'd', 'd' ]
How to modify if condition to deal with multiple non-unique values?

For something like this we should be gunning for constant time 0(n). Sorting is going to give us O(nlogn) at best and is not required.
function findDuplicates(s) {
const seen = {};
const result = [];
for (let c of s.toLowerCase()) {
if(c in seen) {
result.push(c);
}
seen[c] = true; // could be any value doesn't have to be boolean.
}
return result;
}
console.log(findDuplicates('piccdda123dd'));
result
[ 'c', 'd', 'd', 'd' ]
We just need to iterate over the string once and keep a map object (could use es6 Set) that tells us if we've seen this value before. If we see it again and it's already been seen we can append it to the result array.
If you wanted to see each duplicated character only once try
function findDuplicates2(s) {
const seen = {};
for (let c of s.toLowerCase()) {
const ent = seen[c];
seen[c] = (ent === undefined) ? 1 : ent + 1;
}
return Object.entries(seen).filter(([k,v]) => v > 1).map(([k,v]) => k);
}
console.log(findDuplicates2('piccdda123dd'));
// prints ['c', 'd']

SOLVED.
const findDuplicates = (word) => {
let arr = word.toLowerCase();
let sorted_arr = [...arr].slice().sort();
console.log(sorted_arr);
let results = [];
for (let i = 0; i < sorted_arr.length - 1; i++) {
if (sorted_arr[i + 1] == sorted_arr[i]) {
if (!results.includes(sorted_arr[i]))
results.push(sorted_arr[i]);
}
}
return results;
}
ES6 includes IS REALLY powerfull.

Related

Js secret santa claus algoritm

I wanted to make a small script in js that having a list of users, one user has to make a gift to another.
By applying the following constraints:
If "a" is the santa claus and gives a gift to "c" it cannot be the other way around.
So "c" cannot be the santa claus of "a".
It must work with both an even and an odd number of users.
In your opinion, what could be the right approach to use to try to minimize the number of comparisons, that is, speed up the script.
I was thinking something like this to start, but afterwards I'm not sure how to proceed:
let name = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
let a = [...name];
let group1 = [];
let groupSanta = [];
let groupUser = [];
for (var i = 0; i < name.length / 2 - 1; i++) {
let santaClaus = a[Math.floor(Math.random() * a.length)];
a = a.filter(item => item !== santaClaus);
let user = a[Math.floor(Math.random() * a.length)];
a = a.filter(item => item !== user);
group1.push({ santaClaus, user });
}
console.log(a, group1);
You can just randomly sort the array and assign each person to the next one. Then assign the first person to the last one in the array
// Define names
const names = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
// Function to shuffle array
const shuffle = (arr) => {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
const randomNames = shuffle(names);
// Match each person with the next one, folding over at the end
const matches = randomNames.map((name, index) => {
return {
santa: name,
receiver: randomNames[index + 1] || randomNames[0],
}
});
console.log(matches);
let players = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
players = shuffleArray(players)
const matches = players.map((name, index) => {
return {
santa: name,
receiver: players[index + 1] || players[0],
}
});
function shuffleArray(array) {
let currentIndex = array.length, randomIndex
while (currentIndex != 0) {
randomIndex = Math.floor(Math.random() * currentIndex)
currentIndex--
[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]]
}
return array
}
console.log(matches)

Decoding nested objects in Javascript

I need to decode object input to object output as below:
let input = {
a : 'b',
'c.d': 'e',
'c.f': 'g',
'c.h.0':'i',
'c.h.1':'j',
'c.h.2':'k'
}
let output = {
a : 'b',
c: {
d: 'e',
f:'g',
h: ['i', 'j', 'k']
}
}
I have this decoding function, It works well except for arrays. How can I change this function to also support array decoding, and return the above output object
function assembleQueryData(input) {
const output = {};
const keys = Object.keys(input);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const dots = key.split(".");
let last = output;
for (let j = 0; j < dots.length; j++) {
const dot = dots[j];
if (j >= dots.length - 1) {
last[dot] = input[key];
} else {
last[dot] = last[dot] || {};
}
last = last[dot];
}
}
console.log(output);
return output;
}
let input = {
a : 'b',
'c.d': 'e',
'c.f': 'g',
'c.h.0': 'i',
'c.h.1': 'j',
'c.h.2': 'k',
'l.m.0': 'n',
'l.m.1': 'o',
'l.m.2': 'p',
'l.m.q': 'r',
}
const assembleQueryData = (input) => {
const output = {}
const keys = Object.keys(input)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const dots = key.split('.')
let parent = null;
let last = output
for (let j = 0; j < dots.length; j++) {
const dot = dots[j]
if (j >= dots.length - 1) {
if (!/^\d+$/.test(dot) && Array.isArray(last)) {
last = Object.fromEntries(last.map((value, i) => [i, value]));
parent[dots[j - 1]] = last
}
last[dot] = input[key]
} else {
last[dot] = last[dot] || (/^\d+$/.test(dots[j + 1]) ? [] : {})
}
parent = last
last = last[dot]
}
}
return output
}
console.log(assembleQueryData(input));
I dont think there is an easy way to cover all the cases in one go, in this solution we are first creating an object representation of the given input and then replacing the object with an array where ever we deem fit in Depth-first transversal, we here are also taking care of creating proper arrays.
let input = {
'a': 'b',
'c.d': 'e',
'c.f': 'g',
'c.h.0': 'i',
'c.h.1': 'j',
'c.h.2': 'k',
'c.k.0': 'i',
'c.h.a': 'j',
'c.p.0.5.c': 'j',
'd.1': {},
'd.1.6':'cat'
}
console.log(input)
const assembleQueryData = (input) => {
return Object.entries(input).reduce((obj, [key, value]) => {
let keySet = key.split('.');
keySet.reduce((p, k, i) => {
if (keySet.length === (i + 1)) { p[k] = value; }
else { p[k] = p[k] || {}; }
return p[k]
}, obj);
return obj;
}, {});
}
isObjectOrArray = (input) => {
return Array.isArray(input) || typeof input === 'object' && input !== null;
}
// recursive array which get array if object is array
const getArrays = (input) => {
if (!isObjectOrArray(input)) {
return input;
}
let isArray = Array.isArray(input);
if (!isArray) {
let isArray = !Object.keys(input).some(k => isNaN(k));
if (isArray) {
let len = Math.max(...Object.keys(input))
let arr = Array(len + 1).fill(undefined);
input = arr.map((v, i) => input[i]);
}
}
Object.entries(input).forEach(([key, value]) => {
input[key] = getArrays(value);
});
return input
}
let objOutput = assembleQueryData(input)
let output = getArrays(objOutput)
console.log(output)

Combination of all possible values from first array?

How do a combine the array of arrays based on the first array1 or basically group by array1.
Below is the four Array, where i have to form objects based on A and then based on B.
var array1=["A","B"];
var array2=["1","2","3", "4"];
var array3=["N","O","P", "Q"];
var array4=["R"];
Below is how i need :
[ {
'take': 'A',
'take2': '1',
'take3': 'N',
'take4': 'R'
}, {
'take': 'A',
'take2': '2',
'take3': 'N',
'take4': 'R'
}, {
'take': 'A',
'take2': '3',
'take3': 'N',
'take4': 'R'
}, {
'take': 'A',
'take2': '4',
'take3': 'N',
'take4': 'R'
}, {
'take': 'A',
'take2': '1',
'take3': 'O',
'take4': 'R'
}]
This is something i have tried, but not sure how can i loop n number of n arrays
var result = array1.reduce( (a, v) =>
[...a, ...array2.map(x=>v+x)],
[]);
here is a keep it simple solution (if you know how many arrays do you have) :
const possibilities = [];
const ar1length = array1.length;
const ar2length = array2.length;
const ar3length = array3.length;
const ar4length = array4.length;
// Not cleanest solution available but it does the job
for ( let i = 0; i < ar1length; i++) {
for (let j = 0; j < ar2length; j++) {
for (let k = 0; k < ar3length; k++) {
for (let l = 0; l < ar4length; l++) {
possibilities.push({
"take": array1[i],
"take1": array2[j],
"take2": array3[k],
"take3": array4[l]
});
}
}
}
}
Oh and if you want an unknown number of arrays, you may add all of these arrays to an array in order to iterate over it I guess
I've written a function for this task a while ago, takes an arbitrary amount of Arrays and non-arrays and computes all possible combinations
var array1 = ["A", "B"];
var array2 = ["1", "2", "3", "4"];
var array3 = ["N", "O", "P", "Q"];
var array4 = ["R"];
console.log(combinations(array1, array2, array3, array4).join("\n"));
function combinations(...columns) {
const state = [], combinations = [state];
let head = null;
for (let column = 0; column < columns.length; ++column) {
let value = columns[column];
if (Array.isArray(value)) {
if (value.length > 1) {
head = {
next: head,
column,
row: 0
};
}
value = value[0];
}
state[column] = value;
}
let todo = head;
while(todo) {
if (++todo.row === columns[todo.column].length) {
todo.row = 0;
state[todo.column] = columns[todo.column][todo.row];
todo = todo.next;
} else {
state[todo.column] = columns[todo.column][todo.row];
combinations.push(state.slice());
todo = head;
}
}
return combinations;
}
.as-console-wrapper{top:0;max-height:100%!important}
Here's a recursive approach which works for every number of arrays, you just have to call combine(array1, array2, ..., arrayn):
var array1=["A","B"];
var array2=["1","2","3", "4"];
var array3=["N","O","P", "Q"];
var array4=["R"];
function combine(arr1, ...arr2) {
if(arr2.length === 0) return Array.from(arr1, (x) => x.reduce((obj, y, i) => (obj[`take${i}`] = y, obj), {}));
return combine(arr1.flatMap(d => arr2[0].map(v => {
return [...Object.values(d), ...Object.values(v)]
})), ...arr2.slice(1));
}
console.log(combine(array1, array2, array3, array4));

Convert a single dimensional array into a multidimensional array

In Javascript, how to convert a single dimensional array into a multidimensional array of unspecified depth or length.
Example:
let input = ['a','b','b','b','a','a','b','b','b','c','c','a','a','b','b'];
const makeMatrix = () => {}
let output = makeMatrix(input);
// output: ['a',['b','b','b'],'a','a',['b','b','b',['c','c']],'a','a',['b','b']]
What should the makeMatrix function look like to accomplish this task? Assume that values always move in a linear forward direction, but might possibly cut backward. So a always leads to b. An a would never hop to c. But c might drop back down to a.
This is to try to convert heading elements into a table of contents. Making a simple single tier toc is easy, but making a multi tiered one is wracking my brain. I have looked through a number of solutions, but have not seen anything that solves this particular problem.
You could take a level variable and a levels array for pushing unknown elements.
var input = ['a', 'b', 'b', 'b', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'a', 'a', 'b', 'b'],
levels = [[]],
level = 0,
result;
input.forEach(v => {
var l = level;
do {
if (levels[l][0] === v) {
level = l;
levels[level].push(v);
return;
}
} while (l--)
levels[level].push(levels[level + 1] = [v]);
level++;
});
result = levels[0][0];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The dumb eval solution I had in mind, if it's what you wanted it can be made neat...
function toMulti(arr) {
let str = "[";
let level = 1;
const charLevels = { a: 1, b: 2, c: 3 };
arr.forEach(char => {
const charLevel = charLevels[char];
if (level < charLevel) {
for (let i = 0; i < charLevel - level; i++) {
str += "[";
}
}
if (level > charLevel) {
for (let i = 0; i < level - charLevel; i++) {
str += "],";
}
}
level = charLevel;
str += `'${char}',`;
});
for (let i = 0; i < level; i++) {
str += "]";
}
return eval(str);
}
Alternative version, using JSON building/parsing:
const input = ['a', 'b', 'b', 'b', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'a', 'a', 'b', 'b'];
const result = JSON.parse(Object.entries(input).reduce((json, [key, val]) => {
const jsonVal = JSON.stringify(val);
const diff = key > 0 ? val.charCodeAt(0) - input[key - 1].charCodeAt(0) : 0;
if (diff > 0) {
json += ',['.repeat(diff) + jsonVal;
} else if (diff < 0) {
json += ']'.repeat(-diff) + ',' + jsonVal;
} else {
json += (key > 0 ? ',' : '') + jsonVal;
}
return json;
}, '[') + ']'.repeat(input.slice(-1)[0].charCodeAt(0) - input[0].charCodeAt(0) + 1));
console.log(result);
This basically builds a JSON string using Array.reduce on the input array, adding each item and comparing key codes to include the right amount of opening/closing brackets in the process.

there is an string array A , and an string array B . I want to delete elements in A which are not in B

I think i messed somewhere, Here is my code.
var flag;
for (i = 0; i < A.length; i++)
{
flag = 0;
for (j = 0; j < B.length; j++)
{
if (A[i].indexOf(B[j]) != -1)
{
flag = 1;
}
}
if (flag == 0)
{
A.splice(i, 1);
}
}
It gives output not as per my need
Someone please Help me out
I would do the job like this;
//returns intersection of multiple arrays
Array.prototype.intersect = function(...a) {
return [this,...a].reduce((p,c) => p.filter(e => c.includes(e)));
};
var a = [0,1,2,3,4,5],
b = [4,5,6,7,8,9];
a = a.intersect(b);
console.log(a);
You could use a function which generates first an object with all characters as properties and take it as hashtable for the filtering of array1.
function deleteSome(array1, array2) {
var o = Object.create(null);
array2.forEach(function (a) {
o[a] = true;
});
return array1.filter(function (a) {
return this[a];
}, o);
}
var a = 'abcdefgh'.split(''),
b = 'banana'.split('');
console.log(deleteSome(a,b));
Technically, array "a" should have only elements which are present in array "b".
var a = [1,2,3,4];
var b = [4,5,6];
var new_a = [];
a.map(function(v,i,a){
if(b.indexOf(v) !== -1){
new_a.push(v);
}
});
console.log(new_a); //[4]
By this way i can filter as many arrays as you want.
var a = ['A', 'A', 'R', 'S', 'M', 'D', 'E']
var b = ['C', 'X', 'D', 'F']
//you can add as many arrays as you want
/*var c = ['O', 'P', 'D', 'Q']
var d = ['R', 'D', 'D', 'Z']*/
var arrays = [a,b, /*c , d */];
var result = arrays.shift().filter(function(v) {
return arrays.every(function(a) {
return a.indexOf(v) !== -1;
});
});
console.log(JSON.stringify(result));

Categories

Resources