spread object and conditionaly modify one property ES6 - javascript

I have object as below
const myObject = {
"Obj1" :[ {
name:"test",
down:"No"
Up: "Yes",
},
{ }, {}....
],
"Obj2" :[ {}, {}......
],
"Obj3" : [ {}, {}, {....
],
}
I want to clone above object and want to modified "Obj1" if name="test" then make it as Up to "Yes"
Basically I want to conditionally spread object property.

Well the question is a bit unclear. Anyway,
if you want to conditionally update 'up' if 'name' === 'test' from a cloned object:
import { cloneDeep } from lodash/fp;
// you can use also JSON stringify + JSON parse
// spread operator will only shallow copy your object
//clone it
const deepClonedObject = cloneDeep(myObject);
// update items accordingly to your needs
deepClonedObject.obj1 = deepClonedObject.obj1.map(item => (item.name === 'test'
? { ...item, up: 'Yes' }
: item)
)

You can do it combine reduce and map. reduce because it versatile and can return object instead array and map to update properties.
const myObject = {
Obj1: [
{
name:"test",
down:"No",
Up: "No",
},
],
Obj2: [
{
name:"test",
down:"No",
Up: "No",
},
],
Obj3: [
{
name:"test2",
down:"No",
Up: "No",
},
],
};
const updatedObject = Object.keys(myObject).reduce((obj, key) => {
return {
...obj,
[key]: myObject[key].map((newObject) => {
if (newObject.name === 'test') {
newObject.Up = 'Yes';
}
return newObject;
})
};
}, {});
console.log(updatedObject);

Related

Remove empty arrays from an object dinamically [duplicate]

I am getting array like below.
[{},
 {},
{},
{ label: '2015', showLabels: '1,' },
{},
 {},
{},
{ label: ‘2017’, showLabels: '1,' }]
but, I would like to delete empty indexes.
I have tried following to delete. But, Not working as expected.
const filteredFinalYearArr = yearArray.filter(function (el) {
return el != null;
});
Note: This is dynamic data
Any suggestions?
You could filter all the objects which have non-zero number of keys:
let yearArray = [{},{},{},{label:'2015',showLabels:'1,'},{},{},{},{label:'2017',showLabels:'1,'}]
let filtered = yearArray.filter(el => Object.keys(el).length)
console.log(filtered)
See this article about best ways to check if an Object is empty.
const years = [
{},
{},
{},
{ label: '2015', showLabels: '1,' },
{},
{},
{},
{ label: '2017', showLabels: '1,' }
]
const hasValues = obj => {
for(var key in obj) {
if(obj.hasOwnProperty(key)) return true
}
return false
}
const filteredYears = years.filter(y => hasValues(y))
console.log(filteredYears)
Another way is to use reduce to build the array.
const yearArray = [{},{},{},{label:'2015',showLabels:'1,'},{},{},{},{label:'2017',showLabels:'1,'}]
const filteredFinalYearArr = yearArray.reduce((o, i) => {Object.keys(i).length > 0 && o.push(i); return o}, [])
console.log(filteredFinalYearArr)

Sort deeply nested objects alphabetically by keys

We are using vue-i18n and maintain our messages within an js variable. This leads to deeply nested objects or key/value pairs.
const messages = {
en: {
message: {
page1: {
title: "Some title",
button: {
title: "Foo",
},
subpage: {
...
}
},
...
},
},
de: {...},
};
As u can see, without an appropriate sorting this file will be really confusing. My idea is to sort the whole file alphabetically by keys.
Is there an algorithm/code that can be used for this? Or do I have to write it myself?
You can do some recursivity like :
I used the following answer to write the order function
const order = (unordered) => Object.keys(unordered).sort().reduce(
(obj, key) => {
obj[key] = unordered[key];
return obj;
}, {}
);
const message = {
fr: {
message: "Bonjour",
a: 1
},
en: {
message: "Hello",
a: {
c: 1,
b: 2
}
},
es: "Hola"
}
const sortObjectDeeply = (object) => {
for (let [key, value] of Object.entries(object)) {
if (typeof(value) === "object") {
object[key] = sortObjectDeeply(value)
}
}
return order(object)
}
console.log(sortObjectDeeply(message))

Store every selected property from a nested JavaScript object

I have an object that is structured like this:
{
question: 'something',
node: [
{ question: 'something', node: [{ question: 'something' }] },
{ question: 'something' }
]
}
What I want to do is return a certain property from the whole object, for example, return all the question properties like this:
[{question: 'somthing'}, ...]
Below is a recursive function returnProperty, which takes the object, property name and an array as argument. It populates the array with desired property items (questionlist in below code).
function returnProperty(obj, propName, propertList=[]){
if(obj[propName]){
let propObj = {};
propObj[propName]=obj[propName];
propertList.push(propObj);
}
if(obj.node && obj.node.length){
for(let nodeObj of obj.node){
returnProperty(nodeObj, propName, propertList);
}
}
}
let obj = {
question: 'something',
node: [
{ question: 'something', node: [{ question: 'something' }] },
{ question: 'something' }
]
};
let questionList = [];
returnProperty(obj, 'question', questionList);
console.log(questionList);
Here is a solution that works with ANY key values. You can have
{
question: 'yx',
x: [{question: 'x'}]
}
and it will extract successfully.
const x = {
question: 'something',
node: [
{ question: 'something', node: [{ question: 'fun' }] },
{ question: 'something' }
]
}
const keyToCheck = 'question'
const process = (obj) => {
let output = []
for (const [key, value] of Object.entries(obj)) {
if (key.toString() === keyToCheck) {
output.push({[keyToCheck]: value})
continue;
}
if (Array.isArray(value)) {
for (const arrObj of value) {
output = output.concat(process(arrObj))
}
}
}
return output
}
console.log(process(x))
Define a recursive function getSomething(objOrArray) and a storage array results. See if an object or an array is passed in. Apply either Object.keys(objOrArray) or objOrArray.forEach() to step into the object or array,
if this is an array or a key points to an object or array, call itself getSomething
otherwise if the key is "question", push the key-value pair into results.

JS/TS how to iterate thru object array and change values

consider the following array.
routingButtonsHighlighter = [
{vehicle: true},
{userAssignment: false},
{relations: false}
];
What is the best way to build a function which can do the following goals?
1) will set all members to false
2) set chosen member to true ( passed as a parameter )
Absent more specific requirements, this is a bit of a choose-your-own-adventure.
(Note: For brevity this code uses ES6 computed property names and destructuring assignment and ES2018 object spread syntax, all of which can be transpiled by TypeScript.)
If each object has exactly one key
...and you want to mutate the original array and objects
const objects = [ { vehicle: true }, { userAssignment: false }, { relations: false } ];
function selectKey(objects, selectedKey) {
for (let obj of objects) {
const [key] = Object.keys(obj);
obj[key] = key === selectedKey;
}
return objects;
}
selectKey(objects, 'userAssignment');
console.log(objects);
...and you want a new array of new objects
const objects = [ { vehicle: true }, { userAssignment: false }, { relations: false } ];
function selectKey(objects, selectedKey) {
const newObjects = [];
for (let obj of objects) {
const [key] = Object.keys(obj);
newObjects.push({ [key]: key === selectedKey });
}
return newObjects;
}
console.log(selectKey(objects, 'userAssignment'))
...but you really like functional style
const objects = [ { vehicle: true }, { userAssignment: false }, { relations: false } ];
function selectKey(objects, selectedKey) {
return objects.map(obj => {
const [key] = Object.keys(obj);
return { [key]: key === selectedKey };
});
}
console.log(selectKey(objects, 'userAssignment'))
If the objects can have more than one key
...and you want to mutate the original array and objects
const objects = [
{ vehicle: true, relations: false },
{ userAssignment: false, vehicle: true },
{ relations: false, userAssignment: false },
];
function selectKey(objects, selectedKey) {
for (let obj of objects) {
for (let key of Object.keys(obj)) {
obj[key] = key === selectedKey;
}
}
return objects;
}
selectKey(objects, 'userAssignment');
console.log(objects);
...and you want a new array of new objects
const objects = [
{ vehicle: true, relations: false },
{ userAssignment: false, vehicle: true },
{ relations: false, userAssignment: false },
];
function selectKey(objects, selectedKey) {
const newObjects = [];
for (let obj of objects) {
const newObj = {};
for (let key of Object.keys(obj)) {
newObj[key] = key === selectedKey;
}
newObjects.push(newObj);
}
return newObjects;
}
console.log(selectKey(objects, 'userAssignment'))
...but you really like functional style
const objects = [
{ vehicle: true, relations: false },
{ userAssignment: false, vehicle: true },
{ relations: false, userAssignment: false },
];
function selectKey(objects, selectedKey) {
return objects.map(obj =>
Object.keys(obj).reduce((newObj, key) =>
({ ...newObj, [key]: key === selectedKey }),
{}
)
);
}
console.log(selectKey(objects, 'userAssignment'))
You can iterate the array with Array.forEach(), get the key using Object.keys(), compare to the selected key, and set the value accordingly:
const routingButtonsHighlighter = [{vehicle: true}, {userAssignment: false}, {relations: false}];
const select = (arr, selectedKey) =>
arr.forEach((o) => {
const key = Object.keys(o)[0];
o[key] = key === selectedKey;
});
select(routingButtonsHighlighter, 'userAssignment');
console.log(routingButtonsHighlighter);
Creating a method for something like this would be highly specialized, so to abstract it, I've decided to write it like this:
function arrayFlagSinglePropertyTrue(key, arrayofobjects) {
for (let i in arrayofobjects) {
let keys = Object.keys(arrayofobjects[i]);
if (keys[0] == key) {
arrayofobjects[i][keys[0]] = true;
} else {
arrayofobjects[i][keys[0]] = false;
}
}
return arrayofobjects;
}
routingButtonsHighlighter = [
{vehicle: true},
{userAssignment: false},
{relations: false}
];
console.log(arrayFlagSinglePropertyTrue("relations", routingButtonsHighlighter));
Although this will get what you require done, its highly specialized and only works if the objects in the array contain one property or at the very least the first property in the object itself is the one you want to set to flag.
Edit: Some advice:
Uniformity in lists helps avoid the issue you have. By structuring your objects with uniform property names and then acting on the values themselves, you no longer require the use of specialized functions or code in order to modify it. At this point you can rely on fundamental programming logic to change the properties efficiently.
If you get the list from some external source and have no control over it, then you may need to either reorganize it yourself. If you can't then making specialized functions/codes is your last resort.
If possible, take something like this:
routingButtonsHighlighter = [
{vehicle: true},
{userAssignment: false},
{relations: false}
];
Organize it into something like this where the actual object properties are uniform:
let betterStructureObject = [
{ propertyName: "vehicle", status: true },
{ propertyName: "userAssignment", status: false },
{ propertyName: "vehicle", status: false },
]
So you can easily loop over it and not have to worry about writing specialized code.
for (let i in betterStructureObject) {
if (betterStructureObject[i].propertyName == "vehicle")
betterStructureObject[i].status = true;
else betterStructureObject[i].status = false;
}

Is there a simple way to map nested data with Lodash?

For my current project, I'm working with an API that returns data formatted like this:
{
groups: [
{
items: [
{
points: [
{ name: "name1", ... },
{ name: "name2", ... },
{ name: "name3", ... },
...
],
...
},
...
]
},
...
],
...
};
I'd like to create a pure function, mapValues, that takes in an object in the above format, as well as an object mapping each name to a value, and returns the same structure, but with each point containing the value that corresponds to its name.
For example, calling mapValues(data, { name1: "value1", name2: "value2", name3: "value3" }) should return this:
{
groups: [
{
items: [
{
points: [
{ name: "name1", value: "value1", ... },
{ name: "name2", value: "value2", ... },
{ name: "name3", value: "value3", ... },
...
],
...
},
...
]
},
...
],
...
};
Here's my first pass:
function mapValues(data, values) {
return _.extend({}, data, {
groups: _.map(ui.groups, (group) => {
return _.extend({}, group, {
items: _.map(group.items, (item) => {
return _.extend({}, item, {
points: _.map(item.points, (point) => {
return _.extend({ value: values[point.name] }, point);
})
});
})
});
})
});
}
That works, but there's quite a bit of nesting a duplicate code. For my second attempt, I reached for recursion.
function mapValues(data, values) {
return (function recursiveMap(object, attributes) {
if (attributes.length === 0) { return _.extend({ value: values[object.name] }, object); }
let key = attributes[0];
return _.extend({}, object, {
[key]: _.map(object[key], child => recursiveMap(child, attributes.slice(1)))
});
})(ui, ["groups", "items", "points"]);
}
That works too, but it's difficult to read and not very concise.
Is there a cleaner way to recursively map an object using Lodash? Ideally, I'm looking for a functional approach.
Here's a way you can do it using Object.assign and no fancy functions
var data = <your data here>;
var values = <your mapped values>;
data.groups.items.points.map(p=>
Object.assign(p, {value: values[p.name]})
);
This works because arrays and objects are pass by reference. So any modifications to the values will result in the original being changed.
If you don't want to mutate your original dataset, it requires you to use {} as the first argument (to assign to a new, empty object) and show clear read/write paths for each object.
var newData = Object.assign({}, data,
{groups:
{items:
{points: data.groups.items.points.map(p=>
Object.assign({}, p, {value: values[p.name]})
)}
}
}
);
I know you wanted Lodash, but I had same issue some time ago and I've came up with this JSFiddle I am using great tool created by nervgh. It is very simple yet usefull. You can adjust this function to be pure using Object.assign, accept key parameter and tweak it however you want. You get the idea.
function mapValues(data, values) {
var iterator = new RecursiveIterator(data);
for(let {node} of iterator) {
if(node.hasOwnProperty('name')) {
node.value = values[node.name];
}
}
return data;
}

Categories