Decoding nested objects in Javascript - 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)

Related

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

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.

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));

dna pairing, pushing array inside an array

function pair(str) {
var dna = [];
var dnaarr = [];
for(var i = 0; i < str.length; i++) {
if(str[i].indexOf('G') === 0) {
var a = dna.push('C');
}
if(str[i].indexOf('C') === 0) {
var b = dna.push('G');
}
if(str[i].indexOf('A') === 0) {
var c = dna.push('T');
}
if(str[i].indexOf('T') === 0) {
var d = dna.push('A');
}
}
for(var j = 0; j < str.length; j++) {
var e = dnaarr.push(str[j]);
var f = dnaarr.push(dna[j]);
}
return dnaarr;
}
pair("ATGCG");
When I run this code, it returns
[ 'A', 'T', 'T', 'A', 'G', 'C', 'C', 'G', 'G', 'C' ]
I need it to return
[['A', 'T'], ['T', 'A'], ['G', 'C'], ['C','G'], ['G', 'C']]
Could anyone please help me with this code?
Here's a simpler version:
function pair(str)
{
// Array to hold the pairs
var dna = [];
// Loop through the string
for (var i = 0; i < str.length; i++) {
// Switch based on the current letter in the string
// Push an array to dna with the current string and it's pair
// in the case of 'G' the array would be ['G','C']
// dna would then be [['G','C']]
switch(str[i])
{
case "G":
dna.push([str[i],"C"]);
break;
case "C":
dna.push([str[i],"G"]);
break;
case "A":
dna.push([str[i],"T"]);
break;
case "T":
dna.push([str[i],"A"]);
break;
};
}
// return the array
return dna;
}
pair("ATGCG")
It was a problem with your array pushes.
function pair(str) {
var dnaarr = [];
//var dnatot = [];
for(var i = 0; i < str.length; i++) {
var dna = [];
dna.push(str[i]); //pushing current str[i]
if(str[i].indexOf('G') === 0) {
var a = dna.push('C');
}
if(str[i].indexOf('C') === 0) {
var b = dna.push('G');
}
if(str[i].indexOf('A') === 0) {
var c = dna.push('T');
}
if(str[i].indexOf('T') === 0) {
var d = dna.push('A');
}
dnaarr.push(dna); //pushing the array dna to the main array dnaarr
}
return dnaarr;
}
console.log(pair("ATGCG"));
I believe this is very easy to read and understand.
function pairElement(str) {
var pairs = { 'G': 'C', 'C': 'G', 'A': 'T', 'T': 'A' };
return str.split('').map(function(char) {
return [char, pairs[char]];
});
}
pairElement("GCG"); // [['G', 'C'], ['C', 'G'], ['G', 'C']]
EDIT: Code is simplified. On the beginning of the function we create object with possible character pairs. Using split and map array methods, we are pairing every char from str with object pairs.
I found this approach to be the most readable. Explanation in comments
function pairElement(str) {
// base pairs defined by proj requirements
var basePairs = {"A": "T", "T": "A", "C": "G", "G": "C"};
var newGeneCodeArr = [];
for (var i = 0; i < str.length; i++) {
// build individual sequence pair that fits requirements
var newGeneCode = [];
newGeneCode.push(str[i]);
newGeneCode.push(basePairs[str[i]]);
newGeneCodeArr.push(newGeneCode);
}
return newGeneCodeArr;
}
function pairElement(str) {
//convert the string into array of characters
str = str.split('');
//define a multidimensional array to hold pairing
var arr = [];
//using the for loop, we can check for each character
for(var i=0; i<str.length; i++)
{
var tmp = [];
//checking characters and adding pairs inside a tmp array
switch(str[i]){
case 'G':
tmp.push(str[i]);
tmp.push('C');
break;
case 'C':
tmp.push(str[i]);
tmp.push('G');
break;
case 'T':
tmp.push(str[i]);
tmp.push('A');
break;
case 'A':
tmp.push(str[i]);
tmp.push('T');
break;
}
//if tmp has something inside, add it to the multidimensional array
if(tmp.length > 0)
arr.push(tmp);
}
return arr;
}
pairElement("GCG");
Another, probably simpler and cleaner, version of it:
function pair(str) {
var arr = str.split(''),
newArr = [];
arr.forEach(function(e){
if (e == 'A') {
newArr.push([e, 'T']);
} else if (e == 'T') {
newArr.push([e, 'A']);
} else if (e == 'G') {
newArr.push([e, 'C']);
} else {
newArr.push([e, 'G']);
}
});
return newArr;
}
Example:
pair('GATC');
will return:
[['G','C'], ['A','T'], ['T', 'A'], ['C', 'G']]
function pair(str) {
// object of dna paring key, value
var pairing = {
"A": "T",
"T": "A",
"C": "G",
"G": "C"
},
// array to store paired object
final = [];
// make an array by .split and map that array by using the dna paring object
final = str.split('').map(function(val) {
var arr = [];
arr.push(val, pairing[val]);
return arr;
});
// return the array of paired objects
return final;
}

Categories

Resources