I'm trying to build an object given an array of objects
const someArray = [
{
name: 'x.y',
value: 'Something for Y'
},
{
name: 'x.a.z',
value: 'Something for Z'
}
]
to look like this
{
x: {
a: {
z: 'Something for Z'
},
y: 'Something for Y'
}
}
I have this code
const buildObj = data => {
let obj = {}
data.forEach(item => {
let items = item.name.split('.')
items.reduce((acc, val, idx) => {
acc[val] = (idx === items.length - 1) ? item.value : {}
return acc[val]
}, obj)
})
return obj
}
buildObj(someArray)
but it doesn't include the y keypair. what's missing?
What you want to do is create an object, then for each dotted path, navigate through the object, creating new object properties as you go for missing parts, then assign the value to the inner-most property.
const someArray = [{"name":"x.y","value":"Something for Y"},{"name":"x.a.z","value":"Something for Z"}]
const t1 = performance.now()
const obj = someArray.reduce((o, { name, value }) => {
// create a path array
const path = name.split(".")
// extract the inner-most object property name
const prop = path.pop()
// find or create the inner-most object
const inner = path.reduce((i, segment) => {
// if the segment property doesn't exist or is not an object,
// create it
if (typeof i[segment] !== "object") {
i[segment] = {}
}
return i[segment]
}, o)
// assign the value
inner[prop] = value
return o
}, {})
const t2 = performance.now()
console.info(obj)
console.log(`Operation took ${t2 - t1}ms`)
.as-console-wrapper { max-height: 100% !important; }
This is ABD at this point but I did it with a recursive builder of the path.
const someArray = [
{
name: 'x.y',
value: 'Something for Y'
},
{
name: 'x.a.z',
value: 'Something for Z'
}
]
const createPath = (path, value) => {
if(path.length === 1){
let obj = {}
obj[path.shift()] = value
return obj
}
let key = path.shift();
let outObject = {}
outObject[key] = { ...createPath(path, value) }
return outObject
}
const createObjectBasedOnDotNotation = (arr) => {
let outObject = {}
for(let objKey in arr){
const { name, value } = arr[objKey];
let object = createPath(name.split("."), value)
let mainKey = Object.keys(object)[0];
!outObject[mainKey] ?
outObject[mainKey] = {...object[mainKey]} :
outObject[mainKey] = {
...outObject[mainKey],
...object[mainKey]
}
}
return outObject
}
console.log(createObjectBasedOnDotNotation(someArray))
Here's an option using for loops and checking nesting from the top down.
const someArray = [
{
name: 'x.y',
value: 'Something for Y'
},
{
name: 'x.a.z',
value: 'Something for Z'
}
]
function parseArr(arr) {
const output = {};
for (let i = 0; i < arr.length; i++) {
const {name, value} = arr[i];
const keys = name.split('.');
let parent = output;
for (let j = 0; j < keys.length; j++) {
const key = keys[j];
if (!parent.hasOwnProperty(key)) {
parent[key] = j === keys.length - 1 ? value : {};
}
parent = parent[key];
}
}
return output;
}
console.log(parseArr(someArray));
Related
Here is a functon that creates a new object filtered the object:
function filterErrors(errors: any) {
const obj:any = {};
const words = [
'Unknown format',
'There is no number',
] as string[];
for (const [key, value] of Object.entries(errors)) {
const filtered = (value as string[]).filter(
(item: string) => !words.includes(item)
);
if (filtered.length > 0) {
obj[key] = filtered;
}
}
return obj;
}
let errors = {
"1": ["'D54'. Unknown format."],
"2": ["'K53'. There is no number."]
}
console.log(filterErrors(errors));
As result I need to get an object where values are not presenetd in words. But now I got all data.
you can do something like this
const filterErrors = (errors) => {
const words = [
'Unknown format',
'There is no number',
]
return Object.entries(errors).reduce((res, [k, err]) => {
const filtered = err.filter(error => !words.some(w => error.includes(w)))
if(filtered.length > 0){
res[k] = filtered
}
return res
}, {})
}
let errors = {
"1": ["'D54'. Unknown format."],
"2": ["'K53'. There is no number."],
"3": ["'E42'. Another error."],
}
console.log(filterErrors(errors));
You code seems to be TypeScript and I convert it to JavaScript and provide a solution for you
function filterErrors(errors ) {
const obj= {};
const words = [
'Unknown format',
'There is no number'
];
let keys = Object.keys(errors)
keys.forEach(k => {
let contains = false
for(let w of words){
if(errors[k][0].includes(w)){
contains = true;
break;
}
}
if(!contains){
obj[k] = errors[k]
}
})
return obj;
}
let errors = {
"1": ["'D54'. Unknown format."],
"2": ["'K53'. There is no number."],
"3": ["'K54'. Test Data"]
}
console.log(filterErrors(errors));
I am trying to extract id from the below array of objects and so far I am able to give it a go with the below code but it is showing undefined and cannot get id , would you please check the code and adjust to get id out?
const array = [{
contact: {
id: 1,
email: 'roar#gmail.com',
},
date: '2/4/22'
},
{
contact: {
id: 2,
email: 'grr#gmail.com',
},
date: '2/4/22'
}
]
function extractValue(arr, prop) {
let extractedValue = [];
for (let i = 0; i < arr.length; ++i) {
// extract value from property
extractedValue.push(arr[i][prop]);
}
return extractedValue;
}
const result = extractValue(array, 'contact.id');
console.log(result);
A good way to do this is the Array Map method
This will get all the id's from your array
const result = array.map((val) => val.contact.id)
const extractValue = (array, path) => {
const [first, second] = path.split('.')
if (second) return array.map(obj => obj[first][second])
return array.map(obj => obj[first])
}
const res = extractValue(array, 'contact.id')
console.log(res)
// => [ 1, 2 ]
this will support single and double level nested results
function find(val, arr) {
for (let x of arr)
val = val[x];
return val;
}
function extractValue(arr, prop) {
return array.map(x => find(x, prop.split(".")))
}
I have been asked this question in an interview. How to solve this? The object and consoles statements are mentioned. I am not getting how to implement the function findPath?
class Obj {
constructor() {
this.data = {
a: {
b: {
c: 12
}
}
};
}
findPath = (str) => {
let sol = this.data;
for (let key of str.split(".")) {
sol = sol[key];
if (!sol) {
return undefined;
}
}
return JSON.stringify(sol);
};
}
let obj = new Obj();
console.log(obj.findPath("a.b.c"));
console.log(obj.findPath("a.b"));
console.log(obj.findPath("a.b.d"));
console.log(obj.findPath("a.c"));
console.log(obj.findPath("a.b.c.d"));
console.log(obj.findPath("a.b.c.d.e"));
var obj = {
a: {
b: {
c: 1
}
}
}
obj.findPath = function(path) {
const keys = path.split('.');
return keys.reduce((currentPath, key) => {
return currentPath && currentPath[key]
}, this)
}
console.log(obj.findPath('a'))
console.log(obj.findPath('a.b'))
console.log(obj.findPath('a.b.c'))
console.log(obj.findPath('a.b.c.d'))
This could do it
var obj = {
a: {
b: {
c: 1
}
}
}
function findPath(path) {
const paths = path.split('.');
let innerObj = {...obj};
for (let i = 0; i < paths.length; i++) {
innerObj = innerObj && innerObj[paths[i]] || null;
}
return innerObj;
}
console.log(findPath("a.b.c"));
console.log(findPath("a.b"));
console.log(findPath("a.b.d"));
console.log(findPath("a.c"));
console.log(findPath("a.b.c.d"));
console.log(findPath("a.b.c.d.e"));
A simple approach
const obj = {
a: {
b: {
c: 12,
},
},
k: null,
};
const testFunc = (obj, k) => {
const arr = k.split(".");
for (let i = 0; i < arr.length; i++) {
obj = obj[arr[i]];
}
return obj;
};
console.log(testFunc(obj, "k")); //null
console.log(testFunc(obj, "a.b.c")); //12
console.log(testFunc(obj, "a.b.c.d")); //undefined
console.log(testFunc(obj, "f")); //undefined
I have a json object with objects inside of it
such as user: {"name": "tim"} and would like a way to turn that in "user.name": 'tim'
I've tried: Javascript Recursion normalize JSON data
Which does not return the result I want, also tried some packages, no luck
You can use a recursive approach to flatten nested objects, by concatenating their keys, as shown below:
const flattenObject = (obj) => {
const flatObject = {};
Object.keys(obj).forEach((key) => {
const value = obj[key];
if (typeof value === 'object') {
const flatNestedObject = flattenObject(value);
Object.keys(flatNestedObject).forEach((nestedKey) => {
flatObject[`${key}.${nestedKey}`] = flatNestedObject[nestedKey];
});
} else {
flatObject[key] = value;
}
});
return flatObject;
};
const obj = {
user: { name: 'tim' },
};
console.log(flattenObject(obj));
This solution works for any amount of levels.
If your environment does not support Object.keys, you can use for..in instead:
const flattenObject = (obj) => {
const flatObject = {};
for (const key in obj) {
if (!obj.hasOwnProperty(key)) continue;
const value = obj[key];
if (typeof value === 'object') {
const flatNestedObject = flattenObject(value);
for (const nestedKey in flatNestedObject) {
if (!flatNestedObject.hasOwnProperty(nestedKey)) continue;
flatObject[`${key}.${nestedKey}`] = flatNestedObject[nestedKey];
}
} else {
flatObject[key] = value;
}
}
return flatObject;
};
const obj = {
user: { name: 'tim' },
};
console.log(flattenObject(obj));
This can easily be done like so:
const myObject = {
user: {
firstname: "john",
lastname: "doe"
}
}
function normalize(suppliedObject = {}) {
const newObject = {};
for (const key of Object.keys(suppliedObject)) {
for (const subKey of Object.keys(suppliedObject[key])) {
newObject[`${key}.${subKey}`] = suppliedObject[key][subKey];
}
}
return newObject;
}
console.log(normalize(myObject));
Be aware that this only normalizes 1 level deep. You can extend the function to normalize all the way down to the last level.
I'm trying to understand the flow of the functions in this reselect library. I currently use console.log to log the output. Still, it is hard to understand the flow. Make me think how complex functional programming is!!!
How can I intercept the calls and log the function name and the parameters to the console with ES6 decorator or Proxy or any other language features?
function defaultEqualityCheck(a, b) {
return a === b
}
function areArgumentsShallowlyEqual(equalityCheck, prev, next) {
if (prev === null || next === null || prev.length !== next.length) {
return false
}
// Do this in a for loop (and not a `forEach` or an `every`) so we can determine equality as fast as possible.
const length = prev.length
for (let i = 0; i < length; i++) {
if (!equalityCheck(prev[i], next[i])) {
return false
}
}
return true
}
function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {
let lastArgs = null
let lastResult = null
console.log("Entering defaultMemoize");
console.log("###INPUT### defaultMemoize argument func type: " + typeof func);
// we reference arguments instead of spreading them for performance reasons
return function () {
if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) {
// apply arguments instead of spreading for performance.
lastResult = func.apply(null, arguments)
}
lastArgs = arguments
return lastResult
}
}
function getDependencies(funcs) {
const dependencies = Array.isArray(funcs[0]) ? funcs[0] : funcs
if (!dependencies.every(dep => typeof dep === 'function')) {
const dependencyTypes = dependencies.map(
dep => typeof dep
).join(', ')
throw new Error(
'Selector creators expect all input-selectors to be functions, ' +
`instead received the following types: [${dependencyTypes}]`
)
}
return dependencies
}
function createSelectorCreator(memoize, ...memoizeOptions) {
console.log("Entering createSelectorCreator");
console.log("#INPUT# argument memoize name: " + memoize.name);
console.log("#INPUT# argument memoize options: ");
console.log(memoizeOptions);
return (...funcs) => {
let recomputations = 0
const resultFunc = funcs.pop()
const dependencies = getDependencies(funcs)
console.log("##INPUT## argument funcs: ");
console.log(resultFunc);
const memoizedResultFunc = memoize(
function () {
recomputations++
// apply arguments instead of spreading for performance.
return resultFunc.apply(null, arguments)
},
...memoizeOptions
)
console.log("memoizedResultFunc: " + typeof memoizedResultFunc);
// If a selector is called with the exact same arguments we don't need to traverse our dependencies again.
const selector = defaultMemoize(function () {
const params = []
const length = dependencies.length
if (arguments != null)
{
console.log("***INPUT*** arguments: ");
console.log(arguments);
}
for (let i = 0; i < length; i++) {
// apply arguments instead of spreading and mutate a local list of params for performance.
params.push(dependencies[i].apply(null, arguments))
}
// apply arguments instead of spreading for performance.
return memoizedResultFunc.apply(null, params)
})
selector.resultFunc = resultFunc
selector.recomputations = () => recomputations
selector.resetRecomputations = () => recomputations = 0
return selector
}
}
const createSelector = createSelectorCreator(defaultMemoize)
function createStructuredSelector(selectors, selectorCreator = createSelector) {
if (typeof selectors !== 'object') {
throw new Error(
'createStructuredSelector expects first argument to be an object ' +
`where each property is a selector, instead received a ${typeof selectors}`
)
}
const objectKeys = Object.keys(selectors)
return selectorCreator(
objectKeys.map(key => selectors[key]),
(...values) => {
return values.reduce((composition, value, index) => {
composition[objectKeys[index]] = value
return composition
}, {})
}
)
}
const shopItemsSelector = state => state.shop.items
const taxPercentSelector = state => state.shop.taxPercent
const subtotalSelector = createSelector(
shopItemsSelector,
items => items.reduce((acc, item) => acc + item.value, 0)
)
const taxSelector = createSelector(
subtotalSelector,
taxPercentSelector,
(subtotal, taxPercent) => subtotal * (taxPercent / 100)
)
const totalSelector = createSelector(
subtotalSelector,
taxSelector,
(subtotal, tax) => ({ total: subtotal + tax })
)
let exampleState = {
shop: {
taxPercent: 8,
items: [
{ name: 'apple', value: 1.20 },
{ name: 'orange', value: 0.95 },
]
}
}
console.log(subtotalSelector(exampleState))// 2.15
//console.log(taxSelector(exampleState))// 0.172
//console.log(totalSelector(exampleState))// { total: 2.322 }