Get the key of a JS Object from the value - javascript

I am trying to get the key of a JS Object in Typescript from an input value, the problem is that the values are inside an Array.
This is what I have seen in the case that the value is not in an array:
const exampleObject = {
key1: 'Geeks',
key2: 'Javascript'
};
function getKeyByValue(object, value) {
for (var prop in object) {
if (object.hasOwnProperty(prop)) {
if (object[prop] === value)
return prop;
}
}
}
console.log(getKeyByValue(exampleObject,'Geeks')) // key1
This is an example of my object that I want to get the key from:
const exampleObject = {
key1: ['Geeks','test1','test2'],
key2: ['Javascript','test3','test4']
};
function getKeyByValue(object, value) {
for (var prop in object) {
if (object.hasOwnProperty(prop)) {
if (object[prop] === value)
return prop;
}
}
}
console.log(getKeyByValue(exampleObject,'Geeks')) // undefined
I need to get the key without using Array.prototype.includes(), because typing the resulting variable as String gives me an error that it may be undefined.
My problem: I don't know how to go through the array inside the function to find the value and get the key
Update:
What I want is to avoid the possibility of returning an undefined, since the input value will always be inside the object, how can I achieve this?

#segmet
You can use my code
const exampleObject = {
key1: ['Geeks', 'test1', 'test2'],
key2: ['Javascript', 'test3', 'test4']
};
function getKeyByValue(object, value) {
var output = "";
for (var prop in object) {
// finding and removing element from array
object[prop].find(i => {
if (i === value) {
output = prop;
return prop;
}
}
)
}
return output
}
console.log(getKeyByValue(exampleObject, 'Geeks'))

You can achieve this with a single line of code by using 3 JavaScript methods - Object.keys(), Array.find() & Array.indexOf() :
const exampleObject = {
key1: ['Geeks','test1','test2'],
key2: ['Javascript','test3','test4']
};
function getKeyByValue(object, value) {
const res = Object.keys(exampleObject).find(key => exampleObject[key].indexOf(value) !== -1);
return res || ''
}
console.log(getKeyByValue(exampleObject,'Geeks'))

function getKeyByValue(object, value) {
const key = Object.entries(object).filter(([key, val]) => val.includes(value));
return Object.fromEntries(key);
}
Try this function instead
You will get the object matching your keys

You can do something like this and then check for null
const getKeyFromValue = (obj:Object, value:string | string[]) => {
for (let key in obj) {
if (typeof value === 'string') {
if (obj[ key ].includes(value)) {
return key;
}
}
else if (Array.isArray(value)) {
if (obj[ key ].every(val => value.includes(val))) {
return key;
}
}
}
return null;
}

One more try:
const exampleObject = {
key1: ['Geeks','test1','test2'],
key2: ['Javascript','test3','test4']
};
function getKeyByValue(obj, data) {
for (const [key, value] of Object.entries(obj)) {
return (~value.indexOf(data)) ? key : false
}
}
console.log(getKeyByValue(exampleObject,'Geeks'))

Related

Adding dynamic properties to object only if the name is defined

i have a function like this:
const getKeysAs = (key1, key2) => {
return {
[key1]: state.key1,
[key2]: state.key2
}
}
So if state.key1 is 'a' and state.key2 is 'b', calling getKyesAs('one', 'two') would return
{
one: 'a',
two: 'b'
}
Now, if one of the argument is undefined, is there a way to not include it in the returned object ?
You can Conditionally add properties to an Object with object destructuring
const obj = {
...(key1 && { [key1]: state[key1] }),
...(key2 && { [key2]: state[key2] })
};
If some of the args function is undefined, null or 0 (falsy values) then it will no be added to the object.
There is a very scalable way to do it:
const state= {
a: "hello",
}
function getKeysAs (keys) {
return [...arguments].reduce((acc, cur)=> {
const newValue = state[cur] && {[cur]: state[cur]}
return {...acc, ...newValue}
}, {})
}
console.log(getKeysAs("a", "b"))
This way, you can pass as much keys as you need without worrying about scalability & undefined values.
Use Object.assign().
const getKeysAs = (key1, key2) => {
return Object.assign({}, key1 && {[key1]: state[key1]}, key2 && {[key2]: state[key2]});
}
Assuming you actually mean to do state[key1], not state.key1, here is a solution that doesn't create superfluous objects:
const getKeysAs = (...keys) => {
const result = {};
for (const key of keys) {
if (key != null) {
result[key] = state[key];
}
}
return result;
}

dynamically flatten nested array of objects javascript

I'm trying to write a function that will accept a nested object array, and dynamically return the flattened result. arrayProperties.filter() is not returning an array of objects like I expect.
const data = [
{
parKeyA: "parValA",
parKeyA1:
{chiKeyA1: "chiValA1", chiKeyA2: "chiValA2"},
parKeyA2: {chiKeyA3: "chiValA3"}
},
{
parKeyB: "parValB",
parKeyB1:
{chiKeyB1:"chiValB1"}
}
]
flatData = flatNestedObjArray(data);
console.log(flatData);
function flatNestedObjArray(array) {
let flatArray = array.map(element => {
let arrayProperties = Object.entries(element);
//filter not returning array of objects
let nestedObjects = arrayProperties.filter(property => {
const parentValue = property[1];
if (typeof parentValue === "object" && parentValue !== null) {
return parentValue;
}
});
//nestedObjects should be array of objects
let merged = nestedObjects.map(obj => element.concat(obj));
return merged;
});
return flatArray;
}
Expected Result:
const data = [
{
parKeyA: "parValA",
chiKeyA1: "chiValA1",
chiKeyA2: "chiValA2",
chiKeyA2: "chiValA2"
},
{
parKeyB: "parValB",
chiKeyB1:"chiValB1"
}
]
You can use recursion to flatten the objects into a single level object and pass that function to map to get an array of flattened object
const data = [{
parKeyA: "parValA",
parKeyA1: {
chiKeyA1: "chiValA1",
chiKeyA2: "chiValA2"
},
parKeyA2: {
chiKeyA3: "chiValA3"
}
},
{
parKeyB: "parValB",
parKeyB1: {
chiKeyB1: "chiValB1",
chiKeyB2: {}
}
}
]
let flatten = (obj, final = {}) => {
for (let key in obj) {
if (typeof obj[key] === 'object' && obj[key] != null) {
flatten(obj[key], final)
} else {
final[key] = obj[key]
}
}
return final
}
console.log(data.map((v) => flatten(v)))
You can use object property loop using in keyword for each level using recursion
for(var prop in data) {
....
}
I used an old recursion technique to start with a working code
function flatten(data) {
var newData = {};
for(var prop in data) {
if(typeof data[prop] == "object") {
var childs = flatten(data[prop])
for(var cprop in childs){
newData[cprop] = childs[cprop];
}
}else {
newData[prop] = data[prop]
}
}
return newData;
}
for(var i=0;i<data.length;i++)
data[i] = flatten(data[i]);
console.log(data);
You need to handle duplicates
You can use map which will return a array and a recursive function. Have added comment in the code , hopefully that will be useful
const data = [{
parKeyA: "parValA",
parKeyA1: {
chiKeyA1: "chiValA1",
chiKeyA2: "chiValA2"
},
parKeyA2: {
chiKeyA3: "chiValA2"
}
},
{
parKeyB: "parValB",
parKeyB1: {
chiKeyB1: "chiValB1"
}
}
]
/* Recursive function.It will take a object,iterate the object and check if the value of the key is another object. If it is another object then call same recursive function */
function getFlatObj(obj) {
let newObject = {}
function doRecurssion(currObj) {
// iterate through the object
for (let keys in currObj) {
// check if the value is another object
if (typeof currObj[keys] === 'object' && typeof currObj[keys] !== null) {
doRecurssion(currObj)
} else {
// if not another object then add key and value
newObject[keys] = currObj[keys]
}
}
return newObject;
}
return doRecurssion(obj);
}
let flatObj = data.map((item) => {
const acc = {};
for (let keys in item) {
if (typeof item[keys] === 'object' && typeof item[keys] !== null) {
Object.assign(acc, getFlatObj(item[keys]))
} else {
acc[keys] = item[keys]
}
}
return acc;
}, {});
console.log(flatObj)
Object.entries() takes an object and converts it into a two-dimensional array:
let object = {keyA: "valueA", keyB: "valueB", keyC: {kA: "vA", kB: "vB"}};
let array = Object.entries(object);
// array = [["keyA", "valueA"], ["keyB", "valueB"], ["keyC", {kA: "vA", kB: "vB"}]];
Using the above within a for...of loop, each entry can be destructured:
for (let [key, value] of Object.entries(object)) {...
Declare an empty array and iterate through each object literal within the array of objects:
let array = [];
for (let obj of objArray) {...
On each object, declare an empty object and then convert each key/value of each object into a sub-array:
let object = {};
for (let [key, value] of Object.entries(obj)) {...
Check each value of each object literal -- if the value is an object literal...
if (Object.prototype.toString.call(value) == "[object Object]") {...
...iterate through the value and assign each key/value to the empty object...
for (let [k, v] of Object.entries(value)) {
object[k] = v;
}
...otherwise assign the key/value to the empty object...
} else {
object[key] = value;
}
Push the new object to the new array:
array.push(object);
Demo
const data = [{
parKeyA: "parValA",
parKeyA1: {
chiKeyA1: "chiValA1",
chiKeyA2: "chiValA2"
},
parKeyA2: {
chiKeyA3: "chiValA3"
}
},
{
parKeyB: "parValB",
parKeyB1: {
chiKeyB1: "chiValB1"
}
}
];
function subObjToKeyVal(objArr) {
let array = [];
for (let obj of objArr) {
let object = {};
for (let [key, value] of Object.entries(obj)) {
if (Object.prototype.toString.call(value) == "[object Object]") {
for (let [k, v] of Object.entries(value)) {
object[k] = v;
}
} else {
object[key] = value;
}
}
array.push(object);
}
return array;
}
console.log(subObjToKeyVal(data));

How to convert a nested object to a string indexed object

I am trying to convert a nested object to a string indexed object, so I can use Vue to display all properties in an object. For example:
var obj = {
key1: 'value1',
key2: {
key3: {
key5: 'value5',
key6: 'value6'
},
key4: 'value4'
}
};
Should be convert to this:
var obj = {
'key1': 'value1',
'key2.key3.key5': 'value5',
'key2.key3.key6': 'value6',
'key2.key4': 'value4'
}
I tried to walk recursively through the object, but I didn't figure out how to get the correct index value and return both the index and the object at the same time.
What I've tried so far:
// let result = objConversion(obj)
objConversion (obj) {
let resultObject = {}
// Walk through the first level
for (let index in obj) {
if (obj.hasOwnProperty(index)) {
let extractedObj = getObjNameRecursive(obj[ index ], index)
resultObject = { ...resultObject, ...extractedObj }
}
}
return resultObject
}
getObjNameRecursive (obj, name) {
let resultObject = {}
if (typeof obj === 'object') {
// Dive into an object
for (let index in obj) {
if (obj.hasOwnProperty(index)) {
if (typeof obj[ 'index' ] === 'object') {
resultObject = { ...resultObject, ...getObjNameRecursive(obj[ 'index' ], name + '.' + index) }
} else {
resultObject = {...resultObject, [name + '.' + index]: obj[index]}
}
}
}
} else {
// Simple assign if not an object
resultObject[ name ] = obj
}
return resultObject
}
But this gives the result like:
obj = {
'key1': 'value1',
'key2.key3.key5': [object Object],
'key2.key3.key6': [object Object],
'key2.key4': 'value4'
}
The answer in Convert string to an attribute for a nested object in javascript is very close to what I want. But what I want is to get the string of attributes of a nested object.
Is there any better way to do this?
Thanks.
Try this
function convert(obj, key, result) {
if(typeof obj !== 'object') {
result[key] = obj;
return result;
}
const keys = Object.keys(obj);
for(let i = 0; i < keys.length; i++){
const newKey = key ? (key + '.' + keys[i]) : keys[i];
convert(obj[keys[i]], newKey, result);
}
return result;
}
call it
convert(obj, '', {});

Find the key in an object which contains a particular value in an array

I need to find the key(type:array) which contains a specific string. The actual data would look like as provided below
I tried to inverse the particular object with _.invert(). But doesnt make sense as it would convert the entire array to a string
{
"key1": ['asd','yrt','uyt'],
"key2": ['nbm','inb','oiu']
}
The response desired is that with we can fetch the key_name if an array element is provided.
ie if the input is lets 'asd' we need to be able to say that the key was key1
Take the keys and find the element with includes.
const
find = (object, value) => Object.keys(object).find(k => object[k].includes(value)),
object = { key1: ['asd','yrt','uyt'], key2: ['nbm','inb','oiu'] },
key = find(object, 'asd');
console.log(key);
How often are you going to do this? If only once, then:
data = {
"key1": ['asd','yrt','uyt'],
"key2": ['nbm','inb','oiu']
}
needle = 'oiu'
magicKey = Object.keys(data).filter(key => data[key].includes(needle))
Otherwise you're going to want to make a new dictionary that has your possible needles as keys!
const obj = { key1: ['asd','yrt','uyt'], key2: ['nbm','inb','oiu']}
const keyResult = Object.entries(obj).find(element => element[1].includes('asd'))
console.log(keyResult[0] || 'not found')
It might look like this:
let elementToFind = "asd";
let object = {
"key1": ['asd','yrt','uyt'],
"key2": ['nbm','inb','oiu']
}
let output;
for(let key in object) { // iterate over every key in object
if(object.hasOwnProperty(key)) { // check if we don't try to access one of object's prototype fields etc.
if(object[key].includes(elementToFind)) {
// check if object[key] which is array, contains element we look for
// if so, break the loop to prevent it from further searching
output = key;
break;
}
}
}
let search = 'asd'
let obj = {
"key1": ['asd','yrt','uyt'],
"key2": ['nbm','inb','oiu']
}
Object.keys(obj).find(key => obj[key].indexOf(search) > -1)
If there can be multiple such keys use filter instead of find.
One method would be to iterate over the key/value produced from Object.entries, and return the key where the string is found in the value (array).
const obj = {
key1: ['asd','yrt','uyt'],
key2: ['nbm','inb','oiu']
}
function getKey(obj, str) {
for (let [key, arr] of Object.entries(obj)) {
if (arr.includes(str)) return key;
}
return null;
}
console.log(getKey(obj, 'inb'));
console.log(getKey(obj, 'asd'));
You could write a function that recursively returns the path...
const hasKey = (key, data) => ({}).hasOwnProperty.call(data, key);
const typeOf = value => ({}).toString.call(value).replace(/\[object (\w+)]/, '$1');
const isLeaf = value => ['String', 'Boolean', 'Null', 'Undefined', 'Number', 'Date', 'Function'].includes(typeOf(value));
const needle = 'asd';
const haystack = {
"key1": ['asd1','yrt','uyt'],
"key2": ['nbm','inb','oiu'],
key3: {
key4: ['asd'],
}
};
const find = (needle, haystack, path = []) => {
if (isLeaf(haystack)) {
return needle === haystack && path;
}
for (let [key, value] of Object.entries(haystack)) {
const result = find(needle, value, path.concat(key));
if (result) { return result; }
}
};
console.log('find', find(needle, haystack));

Best way to reset all values in an Javascript object

My javascript object looks something like:
$scope.display = {
current: {
key1: 'value1',
key2: ['a', 'b'],
key3: 'value2'
}
}
Upon some events in my code, I would like to reset these values to undefined like below:
$scope.display = {
current: {
key1: undefined,
key2: [],
key3: undefined
}
}
I use libraries like lodash, however i don't see any function that would perform this.
I know how to do this manually, but I was wondering if there is a "Best practices" way of performing this task.
I would create a helper function returning object structure:
function getDisplayObject() {
return {
current: {
key1: undefined, // or you can omit undefined keys
key2: [],
key3: undefined
}
};
}
$scope.display = getDisplayObject();
So later when you need to reset data you would execute $scope.display = getDisplayObject(); again.
Here is my solution with lodash mapValues function:
var $clearObject = function(value) {
if (_.isString(value)) {
return undefined
};
if (_.isArray(value)) {
return [];
};
};
var $scopeDisplay = {
current: {
key1: 'value1',
key2: ['a', 'b'],
key3: 'value2'
}
};
$scopeDisplay.current = _.mapValues($scopeDisplay.current, $clearObject);
console.log($scopeDisplay);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
You would loop the properties of your object like this
for (var key in current){
if (current.hasOwnProperty(key)){
if (typeof current[key] === 'string'){
current[key] = undefined;
} else if (current[key] instanceof Array) {
current[key] = [];
} // else ??? Not sure how you want to handle other types
}
}
Array check subject to some potential problems described in the comments here
What about this?
// If you just want to reset a simple object
let reset = (obj) => {
Object.keys(obj).map(key => {
if (obj[key] instanceof Array) obj[key] = []
else obj[key] = undefined
})
}
// If you want to reset a complex object
let recursiveReset = (obj) => {
Object.keys(obj).map(key => {
// Test if it's an Object
if (obj[key] === Object(obj[key])) {
recursiveReset(obj[key])
return
}
if (obj[key] instanceof Array) obj[key] = []
else obj[key] = undefined
})
}
// So you can simply use
reset($scope.display.current)
// or
recursiveReset($scope.display)
test link
In my Angular controller, I do the following:
$scope.user = {
firstname: "",
lastname: "",
displayname: "",
email: "",
password: "",
passwordConfirm: ""
};
// default state of the form
$scope.default = angular.copy($scope.user);
/**
* Resets the form to its default state
* #return {void}
*/
$scope.reset = function () {
$scope.user = angular.copy($scope.default);
}
Initially the scope is empty, so I clone it, and whenever it needs reset, simply call a function. However, without knowing the scope of your project, it's hard to determine the best way to handle it.
I did it in AngularJS controller as:
function item_object() {
var list_item = {
ID: '',
Title: '',
LastName: '',
Contact: '',
City: ''
};
return list_item;
}
//Define the object for the item
$scope.Item = item_object();
// Reset product details
$scope.clear = function () {
$scope.Item = item_object();
}
In this way you do not keep a copy of empty or default object. So no extra cost.
There is no built in way. You would need to loop through and set the defaults based on type.
var x = {
current: {
key1: 'value1',
key2: ['a', 'b'],
key3: 'value2'
}
};
_(x.current).forEach(
function(value, key, obj) {
var result = undefined;
if (_.isArray(value)) {
result = [];
}
obj[key] = result;
}
);
console.log(x);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script>
Basic idea if you have nested objects
var x = {
current: {
key1: 'value1',
key2: ['a', 'b'],
key3: 'value2',
xxxx: {
keyx1: 'valuex1',
keyx2: ['xa', 'xb'],
keyx3: 'valuex2'
}
}
};
function resetObj (obj) {
_(obj).forEach(
function(value, key, objRef) {
var result = undefined;
if (_.isObject(value)) {
resetObj(objRef[key]);
} else {
if (_.isArray(value)) {
result = [];
}
objRef[key] = result;
}
}
);
}
resetObj(x)
console.log(x);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script>
If you are using Jquery, like it seems you are doing, you can do that:
Preapre an empty template of your object:
var emptyTemplate = {
current: {
key1: undefined,
key2: [],
key3: undefined
}
}
and then run:
$scope.display = $.extend(true, {}, emptyTemplate);
If you want to automate it, define some data binding:
var defaults = {
String: undefined,
Array: [],
bool: false
}
Then loop in your object, like other suggestions:
function resetObj (obj) {
_(obj).forEach(
function(value, key, objRef) {
if (_.isObject(value)) {
resetObj(objRef[key]);
} else {
var myvarType = typeOf value;
objRef[key] = defaults[myvarType];
}
}
);
}
I copied this nesting function from other answer, just to add my two cents.
A good way to deal with this would be to use a class instead of an object literal, like:
class current {
constructor() {
this.key1 = undefined;
this.key2 = [];
this.key3 = undefined;
}
}
And whenever you want to clear all the values you can just create a new instance of this class, like:
$scope.display.current = new current();
function isObject (value) {
return value && typeof value === 'object' && value.constructor === Object;
}
const resetObj = (obj, resetVal) => {
if(!isObject(obj)) return;
for(let i in obj){
if(!obj.hasOwnProperty(i)) continue;
if(!isObject(obj[i])){
obj[i] = resetVal;
}else{
resetObj(obj[i], resetVal);
}
}
};
+++++++++++++++++++++++++Typescript solution+++++++++++++++++++++++++++
private deviceSettingsToReset: RemoteDeviceSetting;
// depp copy of response into deviceSettingsToReset variable which need to use in reset functionality in UI.
this.deviceSettingsToReset = JSON.parse(JSON.stringify(response));
// assign value from original copy.
this.selectedAlgorithm = JSON.parse(JSON.stringify(this.deviceSettingsToReset));

Categories

Resources