Javascript: key/value storage in object with multilevel nesting - javascript

I have been given two arrays:-
var keys = ['a.b', 'a.c.d', 'a.e', 'h[0]'];
var values = [10, 20, {}, 40];
The output that I want is:-
{
a: {
b: 10,
c: {
d: 20
},
e: {}
},
h: [40]
}
What I have tried so far is
let constructObject = (keys, values) => {
let output = {};
for(let i = 0; i<keys.length; i++) {
let props = keys[i].split('.');
for(let j=0; j<props.length;j++) {
if(props.length > (j+ 1)) {
if(output[props[j]] == undefined) {
output[props[j]] = {};
}
} else {
output[props[j]] = values[i];
}
}
}
return output;
}
The above code is not nesting deeper. I tried to store nested level key also using for loop but could not get any way to store keys, As javascript only gives way to store value not nested level keys.

You could separate the pathes into smaller parts, like properties and indices and build new object by respecting the next key and decide if an array or an object has to be taken.
function setValue(target, keys, value) {
const
isWrapped = s => s.startsWith('[') && s.endsWith(']'),
unwrap = s => isWrapped(s) ? s.slice(1, -1) : s,
path = keys.match(/\[[^\]+]\]|[^\.\[\]]+/g),
last = path.pop();
path
.reduce((o, k, i, { [i + 1]: next = last }) =>
o[unwrap(k)] = o[unwrap(k)] || (isWrapped(next) ? [] : {}), target)
[unwrap(last)] = value;
}
var keys = ['a.b', 'a.c.d', 'a.e', 'h[0]'],
values = [10, 20, {}, 40],
i,
l = keys.length,
result = {};
for (i = 0; i < l; i++) setValue(result, keys[i], values[i]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Inconsistency, when returning index of duplicate values

I'm trying to create an algorithm to find duplicate values in a list and return their respective indexes, but the script only returns the correct value, when I have 2 equal elements:
array = [1,2,0,5,0]
result -> (2) [2,4]
Like the example below:
array = [0,0,2,7,0];
result -> (6) [0, 1, 0, 1, 0, 4]
The expected result would be [0,1,4]
Current code:
const numbers = [1,2,0,5,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(numbers.indexOf(avg),numbers.indexOf(avg,n_loop))
};
};
};
return tie;
}
console.log(checkATie(numbers));
if possible I would like to know some way to make this code more concise and simple
Use a Set
return [...new Set(tie)]
const numbers1 = [1,2,0,5,0];
const numbers2 = [0,0,2,7,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(avgList.indexOf(avg),avgList.indexOf(avg,n_loop))
};
};
};
return [...new Set(tie)]
}
console.log(checkATie(numbers1));
console.log(checkATie(numbers2));
I hope this help you.you can use foreach function to check each item of array
var array = [0,0,2,7,0];
var result = [] ;
array.forEach((item , index)=>{
if(array.findIndex((el , i )=> item === el && index !== i ) > -1 ){
result.push(index)
}
})
console.log(result);
//duplicate entries as an object
checkDuplicateEntries = (array) => {
const duplicates = {};
for (let i = 0; i < array.length; i++) {
if (duplicates.hasOwnProperty(array[i])) {
duplicates[array[i]].push(i);
} else if (array.lastIndexOf(array[i]) !== i) {
duplicates[array[i]] = [i];
}
}
console.log(duplicates);
}
checkDuplicateEntries([1,2,0,5,0]);
// hope this will help
Create a lookup object with value and their indexes and then filter all the values which occurred more than once and then merge all indexes and generate a new array.
const array = [1, 2, 0, 5, 0, 1, 0, 2],
result = Object.values(array.reduce((r, v, i) => {
r[v] = r[v] || [];
r[v].push(i);
return r;
}, {}))
.filter((indexes) => indexes.length > 1)
.flatMap(x => x);
console.log(result);

JavaScript Updating a dictionary from array of values using array of keys

I have a dictionary (nested), an array of keys, and an array of values. I need to update the dictionary with the given values using the array of keys as the address. Example:
dict = {"a":{"b":[{"c":1,"d":2},{"c":3,"d":4}]}}
address = [["a","b",0,"c"],["a","b",1,"d"]]
value = [10,40]
The desired output is:
dict = {"a":{"b":[{"c":10,"d":2},{"c":3,"d":40}]}}
How can I do this in JavaScript?
You could take a function for setting the value and reduce the path to the inner property.
function setValue(object, [...keys], value) {
var last = keys.pop();
keys.reduce((o, k) => o[k], object)[last] = value;
}
var dict = { a: { b: [{ c: 1, d: 2 }, { c: 3, d: 4 }] } },
address = [["a", "b", 0, "c"], ["a", "b", 1, "d"]],
value = [10, 40],
i,
l = address.length;
for (i = 0; i < l; i++) setValue(dict, address[i], value[i]);
console.log(dict);
You could do something along these lines:
const dict = {"a":{"b":[{"c":1,"d":2},{"c":3,"d":4}]}},
address = [["a","b",0,"c"],["a","b",1,"d"]],
value = [10,40];
// For each address
address.forEach((path, index) => {
let el = dict;
// For each address fragment
for (let i = 0; i < path.length; i++) {
// If it's not the last fragment
if (i < path.length - 1) {
// Navigate to the next
el = el[ path[i] ];
} else {
// Otherwise, set the value
el[ path[i] ] = value[index];
}
}
});
console.log(JSON.stringify(dict));

How to find unique key with desire array value?

I want to get unique p which c values contain all desire_c value !
So here object's p:1 has c value like 1,2,3 . That is match desire_c array value , so I want to get
{p:1} as final result !
Here I am looping in using for loop :(
var object = [{p:1,c:1},{p:1,c:2},{p:1,c:3},{p:2,c:1},{p:3,c:3}];
var desire_c = [1,2,3];
var helper = {};
for(var o in object) {
var current = object[o];
if(typeof helper[current.p] != 'object') {
helper[current.p] = {};
}
helper[current.p][current.c] = null;
}
for(var c of helper) {
for(var d in desire_c) {
c[desire_c[d]]
}
}
You could take a map for p and a set for each c value and check then if all wanted values are in a set.
var object = [{ p: 1, c: 1 }, { p: 1, c: 2 }, { p: 1, c: 3 }, { p: 2, c: 1 }, { p: 3, c: 3 }],
desire_c = [1, 2, 3],
map = new Map,
result;
object.forEach(({ p, c }) => map.has(p) ? map.get(p).add(c) : map.set(p, new Set([c])));
result = [...map.keys()].filter(p => desire_c.every(c => map.get(p).has(c)));
console.log(result);
You can use this auxiliary function to find the unique value:
function findKey(objects, desire){
const map = {}; // Creates a new object to map all keys to all their values, instead of having an array of objects
objects.forEach(obj => {
map[obj.p] = map[obj.p] || []; // Ensures the key has an array of values before pushing a new value
map[obj.p].push(obj.c);
});
return Object.keys(map).find(key => desire.every(des => map[key].includes(des))); // Tries to find a key that contains all desired values
}
Then, just call it like that:
findKey(object, desire_c); // Returns 1 for your example
Please check below code.
var desire_c = [1,2,3];
var data=[{p:1,c:1},{p:1,c:2},{p:1,c:3},{p:2,c:1},{p:3,c:3}];;
var helper = {},element = "p";
for (var i = 0; i < data.length; i++) {
if (!helper[data[i][element]])
helper[data[i][element]] = [];
helper[data[i][element]].push(data[i]["c"]);
};
for (key in helper) {
if (helper.hasOwnProperty(key)) {
if (helper[key].length){
var arr=helper[key];
if(JSON.stringify(arr)==JSON.stringify(desire_c))
{
console.log({"p":key});
}
}
}
}
const hash = {};
for(const {p, c} of object){
if(!hash[p]){
hash[p] = desire_c;
}
hash[p] = hash[p].filter(n => n !== c);
}
const result = [];
for(const [key, val] of Object.entries(hash))
if(!val.length) result.push(key);
This just goes over every p and c in the array, and removes c from the array stored inside the hashtable under p. This array is initialized to your wanted array, so if all elements are removed from it (!val.length) the corresponding key is the one we are looking for.

Group two arrays based off an if statement?

Very green to programming.
I have two arrays, one with numbers and one with text.
var time = [20,40,60,30,36];
var name = ["jon","tim","jon","jon","andy"];
What I want to do is something like:
If name == jon take all corresponding numbers(time) and group them. So it would take name[0] and time[0] and group it as well as jon[2] and time [2] and put it in the same group and so on and so forth. So I assume I would need to create new arrays but I'm not sure how to make the if statement without actually specifying the place in the array.
If you are looking for an array as result set, you could use a hash table for the indices of the result set.
var time = [20, 40, 60, 30, 36],
names = ["jon", "tim", "jon", "jon", "andy"],
groups = {},
result = [];
names.forEach(function (n, i) {
if (!(n in groups)) {
groups[n] = result.push([n, 0]) - 1;
}
result[groups[n]][1] += time[i];
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Loop over your names and use the same index to get the times. Put them in to a new object, setting the starting value of 0 if that name hasn't yet been used.
var arr_time = [20,40,60,30,36];
var arr_name = ["jon","tim","jon","jon","andy"];
var rst = {};
arr_name.forEach(function( person, index ){
rst[ person] = rst[ person] || 0;
rst[ person] += arr_time[ index ];
});
console.log( rst );
You can reach your desired result with Array#reduce.
let time = [20, 40, 60, 30, 36];
let array = ["jon", "tim", "jon", "jon", "andy"];
const check = (name, arr) => {
return { [name]: arr.reduce((s, a, i) => {
a == name ? s.push(time[i]) : null;
return s;
}, [])};
}
console.log(check('jon', array));
console.log(check('tim', array));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can group the items using Array#reduce.
If you want the sums:
var time = [20,40,60,30,36];
var names = ["jon","tim","jon","jon","andy"];
var result = names.reduce(function(o, name, i) {
o[name] = (o[name] || 0) + time[i];
return o;
}, {});
console.log(result);
If you want to group them in arrays:
var time = [20,40,60,30,36];
var names = ["jon","tim","jon","jon","andy"];
var result = names.reduce(function(o, n, i) {
o[n] = o[n] || [];
o[n].push(time[i]);
return o;
}, {});
console.log(result);
var time = [20,40,60,30,36];
var names = ["jon","tim","jon","jon","andy"];
var final = names.reduce(function(a, b, i) {
if(!(a.hasOwnProperty(b))) {
a[b] = 0;
}
a[b] = a[b] + time[i];
return a;
}, {});
Reduce check how reduce works it helps you to understand in better way.
Hope it helps you !
Here is a solution using for loop. For each index in the names array, I am checking if the result object has that name. If it has that name I am adding the time corresponding to that index, otherwise I am adding that name to that result object and adding time.
const times = [20,40,60,30,36];
const names = ["jon","tim","jon","jon","andy"];
var result = {};
for(var i = 0 ; i < names.length ; ++i){
let name = names[i];
let time = times[i];
if(result[name] !== undefined){
result[name].push(time);
} else {
result[name] = [time];
}
}
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is a solution using array reduce.
var times = [20,40,60,30,36];
var names = ["jon","tim","jon","jon","andy"];
var result = names.reduce((obj, name, index) => {
obj[name] = obj[name] || [];
obj[name].push(times[index]);
return obj;
},{});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

JS calculate mean of same elements in 2d array

I've got a 'table' of two columns represented as an array. The first column are numbers from 1 to 20 and they are labels, the second column are the corresponding values (seconds):
my_array = [ [ 3,4,5,3,4,5,2 ],[ 12,14,16,11,12,10,20 ] ];
I need the mean (average) for each label:
my_mean_array = [ [ 2,3,4,5 ],[ 20/1, (12+11)/2, (14+12)/2, (16+10)/2 ] ];
// edit: The mean should be a float - the notion above is just for clarification.
// Also the number 'labels' should remain as numbers/integers.
My try:
var a = my_array[0];
var b = my_array[1];
m = [];
n = [];
for( var i = 0; a.length; i++){
m[ a[i] ] += b[i]; // accumulate the values in the corresponding place
n[ a[i] ] += 1; // count the occurences
}
var o = [];
var p = [];
o = m / n;
p.push(n);
p.push(o);
How about this (native JS, will not break on older browsers):
function arrayMean(ary) {
var index = {}, i, label, value, result = [[],[]];
for (i = 0; i < ary[0].length; i++) {
label = ary[0][i];
value = ary[1][i];
if (!(label in index)) {
index[label] = {sum: 0, occur: 0};
}
index[label].sum += value;
index[label].occur++;
}
for (i in index) {
if (index.hasOwnProperty(i)) {
result[0].push(parseInt(i, 10));
result[1].push(index[i].occur > 0 ? index[i].sum / index[i].occur : 0);
}
}
return result;
}
FWIW, if you want fancy I've created a few other ways to do it. They depend on external libraries and are very probably an order of magnitude slower than a native solution. But they are nicer to look at.
It could look like this, with underscore.js:
function arrayMeanUnderscore(ary) {
return _.chain(ary[0])
.zip(ary[1])
.groupBy(function (item) { return item[0]; })
.reduce(function(memo, items) {
var values = _.pluck(items, 1),
toSum = function (a, b) { return a + b; };
memo[0].push(items[0][0]);
memo[1].push(_(values).reduce(toSum) / values.length);
return memo;
}, [[], []])
.value();
}
// --------------------------------------------
arrayMeanUnderscore([[3,4,5,3,4,5,2], [12,14,16,11,12,10,20]]);
// -> [[2,3,4,5], [20,11.5,13,13]]
or like this, with the truly great linq.js (I've used v2.2):
function arrayMeanLinq(ary) {
return Enumerable.From(ary[0])
.Zip(ary[1], "[$, $$]")
.GroupBy("$[0]")
.Aggregate([[],[]], function (result, item) {
result[0].push(item.Key());
result[1].push(item.Average("$[1]"));
return result;
});
}
// --------------------------------------------
arrayMeanLinq([[3,4,5,3,4,5,2], [12,14,16,11,12,10,20]]);
// -> [[3,4,5,2], [11.5,13,13,20]]
As suspected, the "fancy" implementations are an order of magnitude slower than a native implementation: jsperf comparison.
var temp = {};
my_array[0].map(function(label, i) {
if (! temp[label])
{
temp[label] = [];
}
temp[label].push(my_array[1][i]);
});
var result = [ [], [] ];
for (var label in temp) {
result[0].push(label);
result[1].push(
temp[label].reduce(function(p, v) { return p + v }) / temp[label].length
);
}
This function do not sort the resulted array like in your result example. If you need sorting, just say me and i will add it.
function getMeanArray(my_array)
{
m = {}; //id={count,value}
for( var i = 0; i<my_array[0].length; i++){
if (m[my_array[0][i]]===undefined)
{
m[my_array[0][i]]={count:0, value:0};
}
m[ my_array[0][i] ].value += my_array[1][i]; // accumulate the values in the corresponding place
m[ my_array[0][i] ].count++; // count the occurences
}
var my_mean_array=[[],[]];
for (var id in m)
{
my_mean_array[0].push(id);
my_mean_array[1].push(m[id].count!=0?m[id].value/m[id].count:0);
}
return my_mean_array;
}

Categories