Looping data from json using Array - javascript

I'm trying to write a function but I doesn't make it. This function works like that
Input: changeSetting("a>b>c","hello")
After that "setting" named value change from {} to {"a":{"b":{"c":"hello"}}}
If input is changeSetting("a","hello") json become {} to {"a":"hello"}
My last code attempt:
function changeSetting(name,val) {
if (name.includes(">")) {
name = name.split('>')
let json = {}
name.map((el,i)=>{
let last = ""
name.filter(el=>!name.slice(i+1).includes(el)).map(el=> {
if(last!="") {
json[el] = {}
}})
})
}
}
How can we make this ? (Optimization not important but if is it good for me)

const changeSetting = (setting, target) => {
if (setting.length < 2) {
return {
[setting]: target
}
} else {
const keys = setting.split('>');
return keys.reduceRight((acc, curr, i) => {
console.log(acc);
if(i === keys.length - 1) {
return acc = {[curr] : target}
}
return acc = { [curr]: acc };
}, {})
}
}
console.log(changeSetting('a', 'hello'));
console.log(changeSetting('a>b>c', 'hello'));

function changeSetting(inputProperties, value) {
let result;
const properties = inputProperties.split(">");
result = `{${properties
.map((property) => `"${property}":`)
.join("{")}"${value}"${"}".repeat(properties.length)}`;
return result;
}
changeSetting("a>b>c", "hello");
changeSetting("a", "hello");

As you work with strings - you may try to use JSON like this:
function changeSetting(name, val) {
const keys = name.split(">");
return JSON.parse(
[
"{",
keys.map((key) => `"${key}"`).join(":{"),
":",
`"${val}"`,
"}".repeat(keys.length),
].join("")
);
}

There's multiple ways to do this, I've commented the snippet
const changeSetting = (name, val) => {
// Split and reverse the name letters
const nameSplit = name.split('>').reverse();
// Set up the inner most object
let newObj = {[nameSplit[0]]:val}
// Now remove the first letter and recurse through the rest
nameSplit.slice(1).forEach((el, idx) => newObj = {[el]: newObj});
console.log(newObj);
}
changeSetting("a>b>c", "hello")
changeSetting("a", "hello")
changeSetting("a>b>c>d>e>f>g", "hello")

You can create an array by splitting name on all > with String.prototype.split(), and then Array.prototype.reduceRight() the created array of elements with an object initial value {} and adding key value pairs but on the last element the value should be variable val.
Code:
const changeSetting = (name, val) => name
.split('>')
.reduceRight((a, c, i, arr) => ({
[c]: i === arr.length - 1 ? val : a
}), {})
console.log(changeSetting('a>b>c', 'hello'))
console.log(changeSetting('a', 'hello'))
console.log(changeSetting('a>b>c>d>e>f>g', 'hello'))

Related

Return all possible combinations of array with optional strings

Lets say I have an array keys = ["the?", "orange", "van", "s?"], with '?' at the end of strings to represent that it is optional.
I want a function in javascript generateCombinations(keys) that returns the possible combinations such as :
[["orange","van"],["the","orange","van"],["orange","van","s"],["the","orange","van","s"]]
One possible way of removing '?' is to simply do a replace("?',"").
I have a feeling it might require a recursive function, which I am not yet quite strong in. Help is appreciated!
So far I've tried this:
function isOptionalKey(key) {
return key.endsWith('?');
}
function hasOptionalKey(keys) {
return keys.some(isOptionalKey);
}
function stripOptionalSyntax(key) {
return key.endsWith('?') ? key.slice(0, -1) : key;
}
function generateCombinations(keys) {
if (keys.length === 1) {
return keys;
}
const combinations = [];
const startKey = keys[0];
const restKeys = keys.slice(1);
if (hasOptionalKey(restKeys)) {
const restCombinations = isOptionalKey(startKey)
? generateCombinations(restKeys)
: restKeys;
if (isOptionalKey(startKey)) {
combinations.push(restCombinations);
}
combinations.push(
restCombinations.map((c) => [stripOptionalSyntax(startKey), ...c])
);
} else {
if (isOptionalKey(startKey)) {
combinations.push(restKeys);
}
combinations.push([stripOptionalSyntax(startKey), ...restKeys]);
}
return combinations;
}
You could take a recursive approach by using only the first item of the array and stop if the array is empty.
const
getCombinations = array => {
if (!array.length) return [[]];
const
sub = getCombinations(array.slice(1)),
optional = array[0].endsWith('?'),
raw = optional ? array[0].slice(0, -1) : array[0],
temp = sub.map(a => [raw, ...a]);
return optional
? [...temp, ...sub]
: temp;
};
keys = ["the?", "orange", "van", "s?"],
result = getCombinations(keys);
console.log(result.map(a => a.join(' ')));

How to test if an array contains arrays? (Jest)

Given the following function:
chunk = (arr, n) => {
return arr.reduce(function(p, cur, i) {
(p[i/n|0] = p[i/n|0] || []).push(cur);
return p;
}, []);
}
I want to test if the output array contains arrays:
test("Splits a given array in N arrays", () => {
let _input = Array.from({length: 999}, () => Math.floor(Math.random() * 999));;
let int = mockService.getRandomArbitrary();
expect(generalService.chunk(_input, int)).toContain(???????);
})
Considering matchers, how can one test if an array contains arrays?
Possibly something like this?
We use Array.isArray() (docs here) to test each element
let output = generalService.chunk(_input, int));
let containArrays = true;
if (output.length) {
output.forEach(element => {
// containArrays is false if one of the element is *not* an array
if (Array.isArray(element) === false) containArrays = false;
});
}
// if output is an empty array it doesn't contain arrays
else containArrays = false;
expect(containArrays).toBeTruthy();
If you use lodash it's very easy
_.any(arr,function(a){
return Array.isArray(a);
})

typescript : logic to covert string array into custom object

Here is my requirement. I was able to achieve to some level in java but we need to move it to typescript (client side).
Note: The below input is for example purpose and may vary dynamically.
Input
var input = ["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"];
We need to create an utility function that takes above input and returns output as below.
Output:
Should be string not an object or anything else.
"{ a { name, type }, b { city {name, zip } , desc }, c }"
any help is much appreciated.
I don't see that typescript plays any role in your question, but here's a solution for constructing the string you requested. I first turn the array into an object with those properties, then have a function which can turn an object into a string formatted like you have
const input = ["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"];
const arrayToObject = (arr) => {
return arr.reduce((result, val) => {
const path = val.split('.');
let obj = result;
path.forEach(key => {
obj[key] = obj[key] || {};
obj = obj[key];
});
return result;
}, {});
}
const objectToString = (obj, name = '') => {
const keys = Object.keys(obj);
if (keys.length === 0) {
return name;
}
return `${name} { ${keys.map(k => objectToString(obj[k], k)).join(', ')} }`;
}
const arrayToString = arr => objectToString(arrayToObject(arr));
console.log(arrayToString(input));
Here's another variation. Trick is to parse the strings recursively and store the intermediate results in an Object.
function dotStringToObject(remainder, parent) {
if (remainder.indexOf('.') === -1) {
return parent[remainder] = true
} else {
var subs = remainder.split('.');
dotStringToObject(subs.slice(1).join('.'), (parent[subs[0]] || (parent[subs[0]] = {})))
}
}
var output = {};
["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"].forEach(function(entry) {
dotStringToObject(entry, output)
});
var res = JSON.stringify(output).replace(/\"/gi, ' ').replace(/\:|true/gi, '').replace(/\s,\s/gi, ', ');
console.log(res)
// Prints: { a { name, type }, b { city { name, zip }, desc }, c }
You could do something like this:
var input = ["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"];
var output = {};
for(var i =0; i < input.length; i+=2){
output[String.fromCharCode(i+97)] = {};
output[String.fromCharCode(i+97)].name = input[i];
output[String.fromCharCode(i+97)].type = input[i+1];
}
console.log(JSON.stringify(output));

How to write a function that inlines object properties

I have the following object:
var ob = {
view: {
name: 'zpo',
params: {
taskId: 3,
zuka: 'v'
}
}
}
I need to have this object in the following form:
{
"view.name":"zpo",
"view.params.taskId":3,
"view.params.zuka":"v"
}
I have written a function that can do that, but the problem is that it requires external variables passed to it. Here is this function:
function inline(o, result, container) {
for (var p in o) {
if (typeof o[p] === "object") {
inline(o[p], result.length > 0 ? result+'.'+p : p, container);
} else {
container[result + '.' + p] = o[p];
}
}
}
var ob = {
view: {
name: 'zpo',
params: {
taskId: 3,
zuka: 'v'
}
}
}
var c = {};
var r = inline(ob, '', c);
Is there any way to write this function to return correct result without the need to pass result and container external variables?
If i understood you correctly, you want to avoid to call your inline() function with "empty" params.
You could catch this case in your function directly:
function inline(o, result, container) {
result = result || '';
container = container || {};
...
}
var r = inline(ob);
you would still need this params for the recursive part of your function.
Here is a version that does not require any parameters.
// Return an array containing the [key, value] couples of an object.
const objEntries = o => Object.keys(o).map(k => [k, o[k]]);
// Create an object from an array of [key, value] couples.
const entriesObj = (entries, init={}) => entries.reduce((result, [key, val]) => {
result[key] = val;
return result;
}, init);
// Reduce on the object entries (as returned by objEntries) with an empty object as
// initialiser.
const inline = (o) => objEntries(o).reduce((result, [key, val]) => {
if(val instanceof Object){
// If the value is an object, recursively inline it.
const inlineVal = inline(val);
// Prefix each property names of inlineVal with the key of val and add the
// properties to the result object.
entriesObj(
objEntries(inlineVal).map(([subK, subVal]) => [key + '.' + subK, subVal]),
result
);
} else {
// If val is not an object, just add it to the result as is.
result[key] = val;
}
// Return the result.
return result;
}, {});
var ob = {
view: {
name: 'zpo',
params: {
taskId: 3,
zuka: 'v'
}
}
}
var r = inline(ob);
console.log(r);
I used arrow functions and destructuring. Old browsers won't support it. If you need to support them, just replace the arrow functions with regular functions and manually destructure the arguments.
javascript is awesome!
function inline(o, result, container) {
result = result || '';
container = container || {};
for (var p in o) {
if (typeof o[p] === "object") {
inline(o[p], result.length > 0 ? result+'.'+p : p, container);
} else {
container[result + '.' + p] = o[p];
}
}
}
var r = inline(ob);

JS getting value of object with key starting with a string

Is there a quick way to get the value of a key starting with a certain string?
Example :
var obj = {
"key123" : 1,
"anotherkey" : 2
}
obj['key1'] // would return 1
obj['ano'] // would return 2
Thanks
You can create a helper function
function findValueByPrefix(object, prefix) {
for (var property in object) {
if (object.hasOwnProperty(property) &&
property.toString().startsWith(prefix)) {
return object[property];
}
}
}
findValueByPrefix(obj, "key1");
As Kenney commented, the above function will return first match.
You could use find on the entries of the object. IF there's a key which starts with, access the index at 1 to get the value.
Object.entries(o).find(([k,v]) => k.startsWith(part))?.[1]
Here's a snippet:
const getValue = (part, o) => Object.entries(o).find(([k, v]) => k.startsWith(part))?.[1]
const obj = {
"key123": 1,
"anotherkey": 2
}
console.log(
getValue('key', obj),
getValue('ano', obj),
getValue('something', obj),
)
Search for the first match of the property name which begins with specified string:
var obj = {
"key123": 1,
"anotherkey": 2,
"somekey" : 3
};
function getObjectValueByPartialName(obj, name){
if (typeof obj !== "object") {
throw new Error("object is required!");
}
for (var prop in obj) {
if (prop.indexOf(name) === 0){
return obj[prop];
}
}
return "no such property!";
}
console.log(getObjectValueByPartialName(obj, 'some')); // output: 3
console.log(getObjectValueByPartialName(obj, 'key1')); // output: 1
We could use the following one-line generic custom method
var obj = {
"key123" : 1,
"anotherkey" : 2
};
const getObjPropForMatchPrefixKey = (object,prefix) => Object.keys(object).filter(item => item.toString().startsWith(prefix))[0];
console.log(obj[getObjPropForMatchPrefixKey(obj,'an')]);
console.log(obj[getObjPropForMatchPrefixKey(obj,'key')]);

Categories