how to create nested object from an array with same key (javascript) - javascript

arr = ["sadik", "arif", "rahman"]
I want to create a nested object with the same key but different value like:
{
subcategory: {
name: sadik
subcategory: {
name: arif
subcategory: {
name: rahman
}
}
}
}
my code:
let arr = ['sadik', 'arif', 'babor']
let obj = {}
arr.forEach((elem) => {
let a = {}
a["subcategory"] = {name:elem}
Object.assign(obj, a)
})
i get only last value:
{
subcategory: {
name:"babor"
}
}

Your code did not work because you were replacing your "subcategory" key value in each iteration. You have change the reference object to the next nested level each time to get the expected output, see the working snippet below:
const arr = ['level-1', 'level-2', 'level-3']
const obj = {}
let refObj = obj;
arr.forEach( ele => {
refObj = refObj['subcategory'] || refObj;
refObj['subcategory'] = { 'name': ele};
})
console.log('output', obj);

Related

How to dynamically add data from an array with objects to a nested array?

I have this set of data that I get dynamically -
This is the data I dynamically get
and my question is how can I get the values from the key, pattern and label and put them in a nested object like this - how should the nested object look like.
My current code is
let mergeTagsObj = {};
const merg = function(arr){
const propertyDataMap = arr.map(x => x.key);
propertyDataMap.forEach(x => {
mergeTagsObj[x] = {}
});
console.log(mergeTagsObj);
// console.log(object);
};
merg(displayArr)
displayArr has the data that I dynamically get, and I map each one to get the key so I can then give the object property a name. But after that I need to get the other 2 (pattern and label) and put it in the mergeTagsObj;
ex: mergeTagsObj = {
firstName:{
name:{label}
value:{pattern}
},
...
};
You can add the pattern and label in your forEach and any other logic that you might need to transform the data.
const data = [{key: 'firstName', pattern: "{{firstName}}", label: "First Name"},
{key: 'lastName', pattern: "{{lastName}}", label: "Last Name"},
{key: 'unsubscribeLink', pattern: "{{unsubscribeLink}}", label: "Unsubscribe Link"}
]
const transformDataToTagsObject = (dData) => {
const dynamicData = {};
dData.forEach((currentData, index) => {
const currentKey = currentData.key
const name = currentData.label
let value = currentData.pattern
if(currentData.key === 'unsubscribeLink'){
value = `<a href='${value}'>Unsubscribe</a>`
}
dynamicData[currentKey] = {
name,
value
}
})
const tagsObject = {
tags: dynamicData
}
return tagsObject;
}
const finalResults = transformDataToTagsObject(data)
console.log(finalResults)
Not most elegant solution, but I think this should work. Don't need to create the array of keys first you can just iterate over the arr of objects.
const merg = function(arr){
arr.forEach(x => {
mergeTagsObj[x.key] = {};
mergeTagsObj[x.key]['name'] = x.label;
mergeTagsObj[x.key]['value'] = x.pattern
});
console.log(mergeTagsObj);
// console.log(object);
};
// Given
const data = [
{key: "firstName", pattern: "{{firstName}}", label: "First Name"},
{key: "unsubscribeLink", pattern: "{{unsubscribeLink}}", label: "Unsubscribe Link"}
];
const tagsObject = data.reduce((obj, item) => {
const key = item.key;
const name = item.label;
let value = item.pattern;
if (key === 'unsubscribeLink') value = 'Unsubscribe';
return {...obj, [key]: {name, value}};
}, {});
console.log(tagsObject);

how to get array according to conditions in javascript

My array comes like this
var data=[{PRODUCT : P1}, {PRODUCT: P2}]
I wantt to convert this into [P1, P2].
Sometimes my array comes like this
var data=[{ITEM: I1, QUANTITY:1}, {ITEM: I2, QUANTITY:2}]
I wantt to convert this into [I1, I2].
so can we make a common function, where I just want to extract particular value of array and make a new array.
p.s. Thank you in advance
I tried to write the logic like this:
data.map((d, index) => { var result= [];
result.includes(d[0]); })
but it,s not dynamic
You could define a function which will always get the first value of the first object key, this should satisfy your needs based on the above
var data1 = [{
ITEM: 'I1',
QUANTITY: 1
}, {
ITEM: 'I2',
QUANTITY: 2
}]
var data2 = [{
PRODUCT: 'P1'
}, {
PRODUCT: ' P2'
}]
function getArrayOfValues(list) {
return list.reduce((acc, x) => {
const firstValue = Object.values(x)[0];
acc.push(firstValue)
return acc;
}, [])
}
const result1 = getArrayOfValues(data1)
console.log(result1)
const result2 = getArrayOfValues(data2)
console.log(result2)
function getProductOrItem(list) {
return list.reduce((accumulator, obj) => {
if (obj.PRODUCT) {
accumulator.push(obj.PRODUCT);
} else if (obj.ITEM) {
accumulator.push(obj.ITEM);
}
return accumulator;
}, [])
}
you can iterate through your array with map() method and inside it extract the value of a first entity of an object in your array and simply get a new array with all values:
const data1 =[{PRODUCT : 'P1'}, {PRODUCT: 'P2'}]
const data2 = [{ITEM: 'I1', QUANTITY: 1}, {ITEM: 'I2', QUANTITY: 2 }]
const transformValuesOfArray = (arrayToTransform) =>
arrayToTransform.map(value => {
const firstObjectValue = Object.values(value)[0]
return firstObjectValue
})
console.log(transformValuesOfArray(data1))
console.log(transformValuesOfArray(data2))

Mark only second duplicated object in js array

I have a list of objects:
[{name: 'Elza'}, {name: 'Tom'}, {name: 'Elza'}]
I use the below methods to get duplicated objects(by name) and assign a prop isDuplicated:
const duplicatedNames = arrayOfObjects
.map(e => e['name'])
.map((e, i, final) => final.indexOf(e) !== i && i++)
.filter(obj => arrayOfObjects[obj])
.map(e => !arrayOfObjects[e]['name']);
const result = arrayOfObjects.filter((obj, i) => {
return duplicatedNames.includes(obj.name) && Object.assign(obj, { isDuplicated: true });
});
I receive an array like:
[{name: 'Elza', isDuplicated: true}, {name: 'Tom'}, {name: 'Elza', isDuplicated: true}]
I would like to mark only the second occurrence of duplicate- so i would like the result to be:
[{name: 'Elza'}, {name: 'Tom'}, {name: 'Elza', isDuplicated: true}]
Can anyone know how to do it base on my code?
Here is a function that checks if a name exist more then once.
let data = [{name:'Elza'}, {name:'Tom'}, {name:'Elza'}, {name: "Jerry"}, {name: "Jerry"}];
function checkDup(arr){
let cache = [];
return arr.map(({name}, index) => {
if(!cache.find(el => el.name == name)){
cache.push({name, index});
return {name, index};
}
let { index: cacheIndex } = cache.find(el => el.name === name);
return {name,index: cacheIndex , isDuplicated: true};
})
}
console.log(checkDup(data));
You could create a Set of names. If the size of the set is same as after the name has been added, then it's duplicate record.
const input = [{name:'Elza'}, {name:'Tom'}, {name:'Elza'}],
names = new Set;
for (const o of input)
if (names.size === names.add(o.name).size)
o.isDuplicate = true
console.log(input)
You can try this:
let users = [{name:'Elza'}, {name:'Tom'}, {name:'Elza'}]
let flags = [], output = [];
users.forEach(user => {
if (flags[user.name]) {
output.forEach(item => {
if (item.name === user.name) {
item.isDuplicated = true
output.push(user);
}
})
} else {
flags[user.name] = true;
output.push(user);
}
})
Given your original array A, you could create a temporary array B and, for each a element of A, check:
if B contains a.name, then set a.isDuplicated to true;
else, push a.name in B.
let A = [{name: 'Elza'}, {name: 'Tom'}, {name: 'Elza'}];
let B = [];
A.forEach(a => {
if (B.includes(a.name)) {
a.isDuplicated = true;
} else {
B.push(a.name);
}
});
console.log(A);
You can use reduce with a helper object:
const collection = [{ name: 'Elza'}, { name: 'Tom'}, { name: 'Elza' }]
const helper = {}
const result = collection.reduce((acc, { name }) => {
if (helper[name]) {
return [...acc, { name, isDuplicate: true }]
}
helper[name] = 'x';
return [...acc, { name }]
}, [])
console.log(result)

Merge two objects by matching key while following Flow rules

I have created a function as shown below, which should append the array value into the first object.
Let's say we have given data below:
subjects = {
student1: ['Math', 'Science'],
student2: ['Math', 'Physics', 'English'],
};
students = {
student1: {
// other data
subjectsList: [],
},
student2: {
// other data
subjectsList: [],
},
};
Function code below:
const merge = (subjects: Object, students: Object) => {
Object.keys(subjects).forEach((id: Object) => {
const subjectsList = subjects[id];
const student = students[id];
if (student) {
const updatedStudent = {
...student,
subjectsList,
};
students[id] = updatedStudent;
}
});
return students;
};
This would result in a flow error:
Cannot access the computed property using object type [1].
app/reducers/subjects.reducer.js:42:32
42| const student = students[id];
^^
References:
app/reducers/subjects.reducer.js:40:48
40| Object.keys(subjects).forEach((id: Object) => {
^^^^^^ [1]
Object.keys(subjects).forEach((id: Object)
The id in the .forEach((id) => is not an Object, but a String
If you remove the typehinting (or whatever it is called).
const merge = (subjects, students) => {
Object.keys(subjects).forEach((id) => {
const subjectsList = subjects[id];
const student = students[id];
if (stand) {
const updatedStudent = {
...student,
subjectsList,
};
students[id] = updatedStudent;
}
});
return students;
};
I think this will work.

Removing object properties with Lodash

I have to remove unwanted object properties that do not match my model. How can I achieve it with Lodash?
My model is:
var model = {
fname: null,
lname: null
}
My controller output before sending to the server will be:
var credentials = {
fname: "xyz",
lname: "abc",
age: 23
}
I am aware I can use
delete credentials.age
but what if I have lots of unwanted properties? Can I achieve it with Lodash?
You can approach it from either an "allow list" or a "block list" way:
// Block list
// Remove the values you don't want
var result = _.omit(credentials, ['age']);
// Allow list
// Only allow certain values
var result = _.pick(credentials, ['fname', 'lname']);
If it's reusable business logic, you can partial it out as well:
// Partial out a "block list" version
var clean = _.partial(_.omit, _, ['age']);
// and later
var result = clean(credentials);
Note that Lodash 5 will drop support for omit
A similar approach can be achieved without Lodash:
const transform = (obj, predicate) => {
return Object.keys(obj).reduce((memo, key) => {
if(predicate(obj[key], key)) {
memo[key] = obj[key]
}
return memo
}, {})
}
const omit = (obj, items) => transform(obj, (value, key) => !items.includes(key))
const pick = (obj, items) => transform(obj, (value, key) => items.includes(key))
// Partials
// Lazy clean
const cleanL = (obj) => omit(obj, ['age'])
// Guarded clean
const cleanG = (obj) => pick(obj, ['fname', 'lname'])
// "App"
const credentials = {
fname:"xyz",
lname:"abc",
age:23
}
const omitted = omit(credentials, ['age'])
const picked = pick(credentials, ['age'])
const cleanedL = cleanL(credentials)
const cleanedG = cleanG(credentials)
Get a list of properties from model using _.keys(), and use _.pick() to extract the properties from credentials to a new object:
var model = {
fname:null,
lname:null
};
var credentials = {
fname:"xyz",
lname:"abc",
age:23
};
var result = _.pick(credentials, _.keys(model));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script>
If you don't want to use Lodash, you can use Object.keys(), and Array.prototype.reduce():
var model = {
fname:null,
lname:null
};
var credentials = {
fname:"xyz",
lname:"abc",
age:23
};
var result = Object.keys(model).reduce(function(obj, key) {
obj[key] = credentials[key];
return obj;
}, {});
console.log(result);
You can easily do this using _.pick:
var model = {
fname: null,
lname: null
};
var credentials = {
fname: 'abc',
lname: 'xyz',
age: 2
};
var result = _.pick(credentials, _.keys(model));
console.log('result =', result);
<script src="https://cdn.jsdelivr.net/lodash/4.16.4/lodash.min.js"></script>
But you can simply use pure JavaScript (specially if you use ECMAScript 6), like this:
const model = {
fname: null,
lname: null
};
const credentials = {
fname: 'abc',
lname: 'xyz',
age: 2
};
const newModel = {};
Object.keys(model).forEach(key => newModel[key] = credentials[key]);
console.log('newModel =', newModel);
Lodash unset is suitable for removing a few unwanted keys.
const myObj = {
keyOne: "hello",
keyTwo: "world"
}
unset(myObj, "keyTwo");
console.log(myObj); /// myObj = { keyOne: "hello" }
Here I have used omit() for the respective 'key' which you want to remove... by using the Lodash library:
var credentials = [{
fname: "xyz",
lname: "abc",
age: 23
}]
let result = _.map(credentials, object => {
return _.omit(object, ['fname', 'lname'])
})
console.log('result', result)
You can use _.omit() for emitting the key from a JSON array if you have fewer objects:
_.forEach(data, (d) => {
_.omit(d, ['keyToEmit1', 'keyToEmit2'])
});
If you have more objects, you can use the reverse of it which is _.pick():
_.forEach(data, (d) => {
_.pick(d, ['keyToPick1', 'keyToPick2'])
});
To select (or remove) object properties that satisfy a given condition deeply, you can use something like this:
function pickByDeep(object, condition, arraysToo=false) {
return _.transform(object, (acc, val, key) => {
if (_.isPlainObject(val) || arraysToo && _.isArray(val)) {
acc[key] = pickByDeep(val, condition, arraysToo);
} else if (condition(val, key, object)) {
acc[key] = val;
}
});
}
https://codepen.io/aercolino/pen/MWgjyjm
This is my solution to deep remove empty properties with Lodash:
const compactDeep = obj => {
const emptyFields = [];
function calculateEmpty(prefix, source) {
_.each(source, (val, key) => {
if (_.isObject(val) && !_.isEmpty(val)) {
calculateEmpty(`${prefix}${key}.`, val);
} else if ((!_.isBoolean(val) && !_.isNumber(val) && !val) || (_.isObject(val) && _.isEmpty(val))) {
emptyFields.push(`${prefix}${key}`);
}
});
}
calculateEmpty('', obj);
return _.omit(obj, emptyFields);
};
For array of objects
model = _.filter(model, a => {
if (!a.age) { return a }
})
Recursively removing paths.
I just needed something similar, not removing just keys, but keys by with paths recursively.
Thought I'd share.
Simple readable example, no dependencies
/**
* Removes path from an object recursively.
* A full path to the key is not required.
* The original object is not modified.
*
* Example:
* const original = { a: { b: { c: 'value' } }, c: 'value' }
*
* omitPathRecursively(original, 'a') // outputs: { c: 'value' }
* omitPathRecursively(original, 'c') // outputs: { a: { b: {} } }
* omitPathRecursively(original, 'b.c') // { a: { b: {} }, c: 'value' }
*/
export const omitPathRecursively = (original, path, depth = 1) => {
const segments = path.split('.')
const final = depth === segments.length
return JSON.parse(
JSON.stringify(original, (key, value) => {
const match = key === segments[depth - 1]
if (!match) return value
if (!final) return omitPathRecursively(value, path, depth + 1)
return undefined
})
)
}
Working example: https://jsfiddle.net/webbertakken/60thvguc/1/
While looking for a solution that would work for both arrays and objects, I didn't find one and so I created it.
/**
* Recursively ignore keys from array or object
*/
const ignoreKeysRecursively = (obj, keys = []) => {
const keyIsToIgnore = (key) => {
return keys.map((a) => a.toLowerCase()).includes(key)
}
const serializeObject = (item) => {
return Object.fromEntries(
Object.entries(item)
.filter(([key, value]) => key && value)
.reduce((prev, curr, currIndex) => {
if (!keyIsToIgnore(curr[0]))
prev[currIndex] =
[
curr[0],
// serialize array
Array.isArray(curr[1])
? // eslint-disable-next-line
serializeArray(curr[1])
: // serialize object
!Array.isArray(curr[1]) && typeof curr[1] === 'object'
? serializeObject(curr[1])
: curr[1],
] || []
return prev
}, []),
)
}
const serializeArray = (item) => {
const serialized = []
for (const entry of item) {
if (typeof entry === 'string') serialized.push(entry)
if (typeof entry === 'object' && !Array.isArray(entry)) serialized.push(serializeObject(entry))
if (Array.isArray(entry)) serialized.push(serializeArray(entry))
}
return serialized
}
if (Array.isArray(obj)) return serializeArray(obj)
return serializeObject(obj)
}
// usage
const refObject = [{name: "Jessica", password: "ygd6g46"}]
// ignore password
const obj = ignoreKeysRecursively(refObject, ["password"])
// expects returned array to only have name attribute
console.log(obj)
let asdf = [{"asd": 12, "asdf": 123}, {"asd": 121, "asdf": 1231}, {"asd": 142, "asdf": 1243}]
asdf = _.map(asdf, function (row) {
return _.omit(row, ['asd'])
})

Categories

Resources