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

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;
}

Related

How to get only true value key pair from array of objects

I have an array of object that is coming from some api.The data i am getting is like this. It has multiple values but i only want to show the user which access he has. Suppose a user have only read access so i want to show the user read key.
[
{
admin: false,
createdAt: "2022-08-21T05:32:20.936Z",
id: 8,
read: false,
write: true,
},
];
So, i want to get only the key value pair from this array which has true values so i can show the keys using Object.keys().
expected result
[{write:true}]
I have tried different array methods but didn't succeed, here how i was thinking to solve this problem but it's only returning last element value.
item.map(tab => {
return Object.keys(tab).reduce((acc: string, key) => {
if (tab[key]) {
acc[key] = tab[key];
}
return acc;
}, {});
}),
You can get rid of reduce by creating an object from filtered entries. Then just filter by true values.
data = [
{
admin: false,
createdAt: "2022-08-21T05:32:20.936Z",
id: 8,
read: false,
write: true,
},
{
admin: false,
createdAt: "1234",
id: 8,
read: true,
write: true,
}
];
out = data.map(item => Object.fromEntries(Object.entries(item).filter(([key, value]) => value === true)));
console.log(out)
if (tab[key]) will be applied on any truthy value not just true, for example, not empty string is a truthy value, any number is a truthy value except zero.
So you need explicitly check if the value equal to true by if (tab[key] === true)
const data = [
{
admin: false,
createdAt: "2022-08-21T05:32:20.936Z",
id: 8,
read: false,
write: true,
},
];
const result = data.map(tab => {
return Object.keys(tab).reduce((acc, key) => {
if (tab[key] === true) {
acc[key] = tab[key];
}
return acc;
}, {});
})
console.log(result)
For shorthand use can use
const result = data.map(tab => Object.entries(tab).reduce((acc, [key, value]) => ({ ...acc, ...(value === true && { [key]: value }) }), {}));
You can get the keys you want by changing the 2nd parameter of the keyFilters function.
let tabs = [
{admin: false,createdAt: "2022-08-21T05:32:20.936Z",id: 8,read: false,write: true},
{admin: false,createdAt: "2022-08-21T05:32:20.936Z",id: 8,read: true,write: true}
];
let keyFilters = function(values, keys){
let filteredKeys = {}
Object.keys(values).map((key, index)=>{
if (keys.includes(key)){
filteredKeys[key] = values[key]
}
})
return filteredKeys;
}
let filters = tabs.map(tab=>keyFilters(tab, ["read", "write"]))
console.log(filters)
output
0:(2) {read: false, write: true}
1:(2) {read: true, write: true

spread object and conditionaly modify one property ES6

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);

Serialize object of params to url

We have input like this
const params = {
id: 1,
filters: {
price: {
min: 101,
max: 300
}
},
sorters: {
sortBy: 'price',
order: 'desc'
}
}
and we want output to look like this id=1&min=101&max=300&sortBy=price&order=desc.
Object is nested. If we have object with just 1 level (no object within objects) then we could simply do
for (const [key, value] of Object.entries(object1)) {
arr.push(`${key}=${value}`);
}
then probably .join array with &. But here in this case this is not enough. My first though was to just inner this for loops like this:
const paramsStringify = (paramsObject) => {
const arr = []
for (const [key, value] of Object.entries(paramsObject)) {
if(typeof value === 'object') {
for (const [key2, value2] of Object.entries(value)) {
if(typeof value2 === 'object') {
for (const [key3, value3] of Object.entries(value2)) {
console.log('3rd lvl', `${key3}: ${value3}`);
arr.push(`${key3}=${value3}`)
}
} else {
console.log('2nd lvl', `${key2}: ${value2}`);
arr.push(`${key2}=${value2}`)
}
}
} else {
console.log('1st lvl', `${key}: ${value}`);
arr.push(`${key}=${value}`)
}
}
console.log('arr', arr.join('&'))
}
this is working fine but code look awful to me. I nested it three times, if I would have even more nested objects I would need to put another for loop there. But the pattern is same for each level of objects. Can I somehow refactor it so it would work even if I have input with 7 nested objects?
You could use a URLSearchParams object and recursively append key value pairs to it. This will automatically encode values.
const params = {
id: 1,
filters: { price: { min: 101, max: 300 } },
sorters: { sortBy: "price", order: "desc" }
}
function getParam(o, searchParam = new URLSearchParams) {
Object.entries(o).forEach(([k, v]) => {
if (v !== null && typeof v === 'object')
getParam(v, searchParam)
else
searchParam.append(k, v)
})
return searchParam
}
const searchParam = getParam(params);
console.log(
searchParam.toString()
)
const params = {
id: 1,
filters: {
price: {
min: 101,
max: 300
}
},
sorters: {
sortBy: 'price',
order: 'desc'
}
}
function add(a, o) {
for (const [key, value] of Object.entries(o)) {
if (typeof value === 'object') add(a, value); // recursion
else a.push(`${key}=${value}`); // only if value not-an-object
}
return a;
}
console.log(add([], params).join("&"))
This is a simple recursive version. You will probably have to tune it if you want to support more complex structures (arrays, duplicate keys, ...)
Assuming your object adheres to your example, use a URLSearchParams object and avoid recursion using a regex
const params = {
id: 1,
filters: {
price: {
min: 101,
max: 300
}
},
sorters: {
sortBy: 'price',
order: 'desc'
}
}
const js = JSON.stringify(params);
const re = /"(\w+)":[^\{]?"?(\w+)"?/g
const usp = new URLSearchParams();
js.match(re).forEach(str => {
const [key,val] = str.split(":");
usp.set(key.replace(/"/g,""),val.replace(/"/g,""))
})
console.log(usp.toString())
You need to create a recursive function if you don't want to program the same logic for each nested object. See https://www.javascripttutorial.net/javascript-recursive-function/ for more information about recursive functions.

Building adjacency matrix from an array of objects

I have a plain JavaScript array of objects, say e.g.
const drawings = [
{
name: "Foo",
category: "widget"
},
{
name: "Bar",
category: "widget"
},
{
name: "Bar",
category: "fidget"
},
]
etc, where both the name and category have duplicates. What I want to end up with is essentially a list of objects (this is to meet the interface for a 3rd party library), where each object represents a name, and then for each category there is a property that is either true or false, depending on the original list. So for the example the output would be:
const output = [
{
name: "Foo",
widget: true,
fidget: false
},
{
{
name: "Bar",
widget: true,
fidget: true
},
]
I would first go through and make an object of your categories with the categories as keys and default values as false.
Then you can assign this to each object and set the correct keys to true as you go through.
const drawings = [{name: "Foo",category: "widget"},{name: "Bar",category: "widget"},{name: "Bar",category: "fidget"},]
// make category object where everything is false
let category_obj = drawings.reduce((a, item) => (a[item.category] = false, a), {})
let output = drawings.reduce((a, {name, category}) => {
// assign cat
if (!a.hasOwnProperty(name)) a[name] = Object.assign({}, {name}, category_obj)
// set to true if the correct category
a[name][category] = true
return a
}, {})
// the above makes an object, but you only want the array of values
console.log(Object.values(output))
If you already know the categories or if you have infered them as you suggested, you could use Array.reduce() like such:
drawings.reduce(function(acc, curr) {
if (!acc.some(elt => elt.name === curr.name)) {
acc.push({name: curr.name, widget: false, fidget: false})
}
const i = acc.findIndex(elt => elt.name === curr.name)
acc[i][curr.category] = true
return acc
}, [])

check if a key exists with certain "value" exists in an object using Javascript

{ foo: [ foo1: true ],
bar: [ bar1: true, bar2: true ],
foobar: [ foobar1: true ] }
Here is an object with values as array-like-objects. I want to find whether a key exist with its value as [ bar1: true, bar2: true ]. If a key is associated with the value, then return the key.
I repeat, I am searching the key associated with the value given in an object.
First, that's the right syntax:
var map = {
foo: { foo1: true },
bar: { bar1: true, bar2: true },
foobar: { foobar1: true }
};
Now to look it up use this function:
function findKey(map, term) {
var found = [];
for(var property in map) {
if(map.hasOwnProperty(property)) {
for(var key in map[property]) {
if(map[property].hasOwnProperty(key) && key === term) {
found.push(property);
}
}
}
}
return found;
}
For example:
var results = findKey(map, 'bar1');
console.log(results);
// [ 'bar' ]
You can convert array-like-object to normal array with Array.prototype.slice.call()
function findKey(obj, key) {
for (var prop in obj) {
var arrLikeObj = obj[prop];
var arr = Array.prototype.slice.call(arrLikeObj);
if (arr.indexOf(key) != -1) {
return prop;
}
}
}

Categories