Test if value of key in array exists - javascript

I have the following object:
[ { id:
'/subscriptions/resourcegroups/coco-test/providers/microsoft.devtestlab/schedules/shutdown-computevm-runscripts2',
name: 'shutdown-computevm-runscripts2' },
{ id:
'/subscriptions/resourcegroups/coco-test/providers/microsoft.devtestlab/schedules/shutdown-computevm-packer',
name: 'shutdown-computevm-packer' } ]
I am trying to write a script that does something if it can't find a specific value in the name key of any of this object.
Example: shutdown-computevm-test
If there is no name anywhere in this object that matches this value, then I want my code to do something.
I'm new to nodejs, I tried things like includes(), indexOf etc, but these are either not the correct ways to do it or I never got the syntax right.
Any hints are appreciated.

Something like this should work for you;
const result = [ { id:
'/subscriptions/resourcegroups/coco-test/providers/microsoft.devtestlab/schedules/shutdown-computevm-runscripts2',
name: 'shutdown-computevm-runscripts2' },
{ id:
'/subscriptions/resourcegroups/coco-test/providers/microsoft.devtestlab/schedules/shutdown-computevm-packer',
name: 'shutdown-computevm-packer' } ];
const found = result.some((part) => part.name === 'shutdown-computevm-test');
if (! found) {
// do something here
}
I prefer it to filter as it won't wait to iterate over all items in the array and will shortcut as soon as it is found.

Use Array.prototype.find()
const arr = [ { id:
'/subscriptions/resourcegroups/coco-test/providers/microsoft.devtestlab/schedules/shutdown-computevm-runscripts2',
name: 'shutdown-computevm-runscripts2' },
{ id:
'/subscriptions/resourcegroups/coco-test/providers/microsoft.devtestlab/schedules/shutdown-computevm-packer',
name: 'shutdown-computevm-packer' } ]
let rs = arr.find(item => item.name === 'shutdown-computevm')
let rs2 = arr.find(item => item.name === 'shutdown-computevm-runscripts2')
console.log(rs) // undefined
console.log(rs2) // obj

Related

Get values from objects in array with rest parameter

let newValues = user.info.socialMedia ? [...user.info.socialMedia] : []
So here we get several objects into the array that have an id key, and instead of the objects themselves I want to have only the id strings in this array. How can I get this working? I am quite new to Javascript, so it would be nice to get some help here.
I tried a for of then a for in loop to get the ids out of there and push them to the array, tho this does not work for my case.
instead of the objects themselves I want to have only the id strings
That sounds like a .map() operation, to project one array into another by applying a transformation to each object. Something like this:
let newValues = user.info.socialMedia ? user.info.socialMedia.map(s => s.id) : []
Or perhaps simpler:
let newValues = user.info.socialMedia?.map(s => s.id) ?? []
For example:
let user = {
info: {
socialMedia: [
{ id: 1, name: 'Ron' },
{ id: 2, name: 'Tammy' },
{ id: 3, name: 'Tammy 2' }
]
}
};
let newValues = user.info.socialMedia?.map(s => s.id) ?? []
console.log(newValues);

Checking an object that contains arrays of objects for a value

I see this question has been touched on a lot on stack overflow but I can't seem to find one that helps my issue. I have an object that contains many arrays with objects nested inside. I need to find the key of the array that contains the object that a usersID. Ive tried .filter and for in loops but I keep getting errors.
My data looks like this :
{
FL: [{id: "mp-2d24973e-c610-4b1c-9152...}{id...}],
TX: [{id: "mp-2d24973e-c610-4b1c-9152...}{id...}],
LA: [{id: "mp-2d24973e-c610-4b1c-9152...}{id...}],
}
Is there a method that allows me to check for a value inside the arrays and if it is found returns the key to that array ie. FL or TX?
const practices = data.items
for (const [key, value] of Object.entries(practices)){
console.log(key, value, "KEYVALUE")
if(value.id === currentUser.currentPracticeID){
console.log(key)
}
}
Of course this code doesnt work but this what Ive tried so far. Im still new to dev so any point in the right direction would be great.
A simple filter() inside of a for...in loop would do it. After looking at the other answers, I should say this will return the first matching ID, rather than an array of all matching ID's like #Barmars answer
const practices = {
FL: [{id: "mp-2d24973e-c610-4b1c-9152"},{id:"3"}],
TX: [{id: "mp-2d24973e-c610-4b1c-9153"},{id:"4"}],
LA: [{id: "mp-2d24973e-c610-4b1c-9154"},{id:"5"}],
}
const findUserKey = (u) => {
for (const key in practices) {
if (practices[key].filter(mp => mp.id === u.currentPracticeID).length>0) return key
}
return false;
}
let currentUser = {currentPracticeID: "mp-2d24973e-c610-4b1c-9154"}
let check_mp = findUserKey(currentUser)
console.log(check_mp)
You can use Object#keys to get the list of object keys, Array#find to iterate over this list, and Array#findIndex to check if the array at each iteration has the userId you're searching for:
const getKeyOfUserId = (obj = {}, userId) =>
Object.keys(obj).find(key =>
obj[key].findIndex(({ id }) => id === userId) >= 0
);
const obj = {
FL: [{id: "mp-2d24973e-c610-4b1c-9152"},{id:"mp-2d24973e-c610-4b1c-9153"}],
TX: [{id: "mp-2d24973e-c610-4b1c-9154"},{id:"mp-2d24973e-c610-4b1c-9155"}],
LA: [{id: "mp-2d24973e-c610-4b1c-9156"},{id:"mp-2d24973e-c610-4b1c-9157"}]
};
console.log( getKeyOfUserId(obj, "mp-2d24973e-c610-4b1c-9156") );
filter() is an array method, practices is an object. You need to use Object.entries() to get an array of keys and values.
Then you can use .some() to test if any of the objects in the nested array contain the ID you're looking for.
const practices = {
FL: [{
id: "mp-2d24973e-c610-4b1c-9152"
}, {
id: "mp-2d24973e-c610-4b1c-9153"
}],
TX: [{
id: "mp-2d24973e-c610-4b1c-9154"
}, {
id: "mp-2d24973e-c610-4b1c-9155"
}],
LA: [{
id: "mp-2d24973e-c610-4b1c-9156"
}, {
id: "mp-2d24973e-c610-4b1c-9157"
}]
};
let currentPracticeID = "mp-2d24973e-c610-4b1c-9156";
const states = Object.entries(practices).filter(([key, arr]) => arr.some(({
id
}) => id == currentPracticeID)).map(([key, arr]) => key);
console.log(states);
Here is how I would do it:
const data = {
FL: [{id: 'ab'}, {id: 'cd'}, {id: 'ef'}],
TX: [{id: 'hi'}, {id: 'jk'}, {id: 'lm'}],
LA: [{id: 'no'}, {id: 'pq'}, {id: 'rs'}]
};
const findKey = (id) => {
let foundKey;
Object.entries(data).some(([key, objects]) => {
if (objects.find(object => object.id === id)) {
foundKey = key;
return true;
}
});
return foundKey;
};
console.log(`The data: ${JSON.stringify(data)}`);
console.log(`Looking for object with ID "jk": ${findKey('jk')}`);
You loop through the entries of your data, so you have the key and the objects for that key. You simply use objects.find to see which object matches the ID you're looking for. I would use array.some for this as it stops the loop when you return true, and foundKey will simply be falsy if nothing is found.

Access properties from array of objects inside looping in Typescript

I have an array with a combination of strings and objects. Then I tried to display the properties of the array based on the key in a loop. Is it possible to do this in typescript?
This is the sample code:
const arr = [
{ name: 'John' },
'Amber',
{ name: 'Cathrine' },
'Louis',
{ name: 'Mike' }
]
arr.forEach((item, key) => {
if (typeof item !== 'string') {
console.log(item.name) // Works
console.log(arr[key].name) // Doesn't work, losing type
}
}
Please check this link to run the code:
https://www.typescriptlang.org/play?#code/MYewdgzgLgBAhgJwTAvDA2gKBjA3jMOAWwFMAuGAcgCkQALMSmAXwBpsqBBIgIxIUrsc+QqQqUAwnCh0EASzAkmbDpQAyIAK5yIgjiOLkqAWTkBrJS0wBdTJkQIAdADMQCAKJxgdABQ+5UCRErDAWAJ4AlKgAfHgccs4wPlBhAA4kIIkBQTAAhCholNDyYADmlFG4HDigkCAANiSO9SCl-oFEjqIkUQD0vTAA6m5mENUwtRANTS1tDujh1l2GfQMAIiAkEIywAO4jIS0gEAqlMCnpHMyYzEA
I really appreciate your help, Thank you!
This is how type guards work in TypeScript. They only filter the type of variable that is checked. In your case you are checking for item (in typeof item !== 'string') and therefore only item is assumed to be not a string.
Fix
If you want to access arr[key].name then store that in a variable, type check that variable with a type guard, and then you are good to go.
Full fixed example:
const arr = [
{ name: 'John' },
'Amber',
{ name: 'Cathrine' },
'Louis',
{ name: 'Mike' }
]
arr.forEach((item, key) => {
const member = arr[key];
if (typeof member !== 'string') {
console.log(member.name) // Okay
}
});

javascript string comparison issue in array.filter()

I have an array which contains following objects.
myArray = [
{ item: { id: 111557 } },
{ item2: { id: 500600 } }]
and I have a variable
targetItemID = '111557'
Note that one is string, and the ones in array are numbers. I'm trying to get the object having the correct item id.
Here is what I have tried,
myArray = [
{ item: { id: 111557 } },
{ item2: { id: 500600 } }]
targetItemID = '111557'
var newArray = myArray.filter(x => {
console.log(x.item.id.toString())
console.log(targetItemID.toString())
x.item.id.toString() === itemID.toString()
})
console.log(newArray);
I expect all matching objects to be added to 'newArray'. I tried to check the values before comparison, They are both strings, they seem exactly same, but my newArray is still empty.
Your second object doesn't have an item property and should.
You need a return in your filter function.
You must compare x.item.id against targetItemID, not itemID. Since you are using console.log() you would have seen and error of itemID id not defined ;).
myArray = [
{ item: { id: 111557 } },
{ item: { id: 500600 } }
];
targetItemID = '111557'
var newArray = myArray.filter(x => {
//console.log(x.item.id.toString())
//console.log(targetItemID.toString())
return x.item.id.toString() === targetItemID.toString();
});
console.log(newArray);
There are a few issues here. First, not all your objects have an item property, so you'll need to check it exists. Second, you're comparing them against a non-existent itemID instead of targetItemID, and finally, and #bryan60 mentioned, if you open a block in an anonymous lambda, you need an explicit return statement, although, to be honest, you really don't need the block in this case:
var newArray =
myArray.filter(x => x.item && x.item.id && x.item.id.toString() === targetItemID)
you need to return for filter to work:
return x.item.id.toString() === itemID.toString();

find and modify deeply nested object in javascript array

I have an array of objects that can be of any length and any depth. I need to be able to find an object by its id and then modify that object within the array. Is there an efficient way to do this with either lodash or pure js?
I thought I could create an array of indexes that led to the object but constructing the expression to access the object with these indexes seems overly complex / unnecessary
edit1; thanks for all yours replies I will try and be more specific. i am currently finding the location of the object I am trying to modify like so. parents is an array of ids for each parent the target object has. ancestors might be a better name for this array. costCenters is the array of objects that contains the object I want to modify. this function recurses and returns an array of indexes that lead to the object I want to modify
var findAncestorsIdxs = function(parents, costCenters, startingIdx, parentsIdxs) {
var idx = startingIdx ? startingIdx : 0;
var pidx = parentsIdxs ? parentsIdxs : [];
_.each(costCenters, function(cc, ccIdx) {
if(cc.id === parents[idx]) {
console.log(pidx);
idx = idx + 1;
pidx.push(ccIdx);
console.log(pidx);
pidx = findAncestorsIdx(parents, costCenters[ccIdx].children, idx, pidx);
}
});
return pidx;
};
Now with this array of indexes how do I target and modify the exact object I want? I have tried this where ancestors is the array of indexes, costCenters is the array with the object to be modified and parent is the new value to be assigned to the target object
var setParentThroughAncestors = function(ancestors, costCenters, parent) {
var ccs = costCenters;
var depth = ancestors.length;
var ancestor = costCenters[ancestors[0]];
for(i = 1; i < depth; i++) {
ancestor = ancestor.children[ancestors[i]];
}
ancestor = parent;
console.log(ccs);
return ccs;
};
this is obviously just returning the unmodified costCenters array so the only other way I can see to target that object is to construct the expression like myObjects[idx1].children[2].grandchildren[3].ggranchildren[4].something = newValue. is that the only way? if so what is the best way to do that?
You can use JSON.stringify for this. It provides a callback for each visited key/value pair (at any depth), with the ability to skip or replace.
The function below returns a function which searches for objects with the specified ID and invokes the specified transform callback on them:
function scan(id, transform) {
return function(obj) {
return JSON.parse(JSON.stringify(obj, function(key, value) {
if (typeof value === 'object' && value !== null && value.id === id) {
return transform(value);
} else {
return value;
}
}));
}
If as the problem is stated, you have an array of objects, and a parallel array of ids in each object whose containing objects are to be modified, and an array of transformation functions, then it's just a matter of wrapping the above as
for (i = 0; i < objects.length; i++) {
scan(ids[i], transforms[i])(objects[i]);
}
Due to restrictions on JSON.stringify, this approach will fail if there are circular references in the object, and omit functions, regexps, and symbol-keyed properties if you care.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_native_JSON#The_replacer_parameter for more info.
As Felix Kling said, you can iterate recursively over all objects.
// Overly-complex array
var myArray = {
keyOne: {},
keyTwo: {
myId: {a: '3'}
}
};
var searchId = 'myId', // Your search key
foundValue, // Populated with the searched object
found = false; // Internal flag for iterate()
// Recursive function searching through array
function iterate(haystack) {
if (typeof haystack !== 'object' || haystack === null) return; // type-safety
if (typeof haystack[searchId] !== 'undefined') {
found = true;
foundValue = haystack[searchId];
return;
} else {
for (var i in haystack) {
// avoid circular reference infinite loop & skip inherited properties
if (haystack===haystack[i] || !haystack.hasOwnProperty(i)) continue;
iterate(haystack[i]);
if (found === true) return;
}
}
}
// USAGE / RESULT
iterate(myArray);
console.log(foundValue); // {a: '3'}
foundValue.b = 4; // Updating foundValue also updates myArray
console.log(myArray.keyTwo.myId); // {a: '3', b: 4}
All JS object assignations are passed as reference in JS. See this for a complete tutorial on objects :)
Edit: Thanks #torazaburo for suggestions for a better code.
If each object has property with the same name that stores other nested objects, you can use: https://github.com/dominik791/obj-traverse
findAndModifyFirst() method should solve your problem. The first parameter is a root object, not array, so you should create it at first:
var rootObj = {
name: 'rootObject',
children: [
{
'name': 'child1',
children: [ ... ]
},
{
'name': 'child2',
children: [ ... ]
}
]
};
Then use findAndModifyFirst() method:
findAndModifyFirst(rootObj, 'children', { id: 1 }, replacementObject)
replacementObject is whatever object that should replace the object that has id equal to 1.
You can try it using demo app:
https://dominik791.github.io/obj-traverse-demo/
Here's an example that extensively uses lodash. It enables you to transform a deeply nested value based on its key or its value.
const _ = require("lodash")
const flattenKeys = (obj, path = []) => (!_.isObject(obj) ? { [path.join('.')]: obj } : _.reduce(obj, (cum, next, key) => _.merge(cum, flattenKeys(next, [...path, key])), {}));
const registrations = [{
key: "123",
responses:
{
category: 'first',
},
}]
function jsonTransform (json, conditionFn, modifyFn) {
// transform { responses: { category: 'first' } } to { 'responses.category': 'first' }
const flattenedKeys = Object.keys(flattenKeys(json));
// Easily iterate over the flat json
for(let i = 0; i < flattenedKeys.length; i++) {
const key = flattenedKeys[i];
const value = _.get(json, key)
// Did the condition match the one we passed?
if(conditionFn(key, value)) {
// Replace the value to the new one
_.set(json, key, modifyFn(key, value))
}
}
return json
}
// Let's transform all 'first' values to 'FIRST'
const modifiedCategory = jsonTransform(registrations, (key, value) => value === "first", (key, value) => value = value.toUpperCase())
console.log('modifiedCategory --', modifiedCategory)
// Outputs: modifiedCategory -- [ { key: '123', responses: { category: 'FIRST' } } ]
I needed to modify deeply nested objects too, and found no acceptable tool for that purpose. Then I've made this and pushed it to npm.
https://www.npmjs.com/package/find-and
This small [TypeScript-friendly] lib can help with modifying nested objects in a lodash manner. E.g.,
var findAnd = require("find-and");
const data = {
name: 'One',
description: 'Description',
children: [
{
id: 1,
name: 'Two',
},
{
id: 2,
name: 'Three',
},
],
};
findAnd.changeProps(data, { id: 2 }, { name: 'Foo' });
outputs
{
name: 'One',
description: 'Description',
children: [
{
id: 1,
name: 'Two',
},
{
id: 2,
name: 'Foo',
},
],
}
https://runkit.com/embed/bn2hpyfex60e
Hope this could help someone else.
I wrote this code recently to do exactly this, as my backend is rails and wants keys like:
first_name
and my front end is react, so keys are like:
firstName
And these keys are almost always deeply nested:
user: {
firstName: "Bob",
lastName: "Smith",
email: "bob#email.com"
}
Becomes:
user: {
first_name: "Bob",
last_name: "Smith",
email: "bob#email.com"
}
Here is the code
function snakeCase(camelCase) {
return camelCase.replace(/([A-Z])/g, "_$1").toLowerCase()
}
export function snakeCasedObj(obj) {
return Object.keys(obj).reduce(
(acc, key) => ({
...acc,
[snakeCase(key)]: typeof obj[key] === "object" ? snakeCasedObj(obj[key]) : obj[key],
}), {},
);
}
Feel free to change the transform to whatever makes sense for you!

Categories