how can I not release data if two attributes are empty?
const fork = [
{ from: 'client', msg: null, for: null },
{ from: 'client', msg: '2222222222222', for: null },
{ from: 'server', msg: 'wqqqqqqqqqqqq', for: 'data/64.....' }
];
console.log(message)
These are three sample entries, in fact they are always replenished. And there are more.
I need to do this, if two attributes are empty then do not send if somewhere null then do not release this value
const fork = [
{ from: 'client', msg: null, for: null }, // remove line full
{ from: 'client', msg: '2222222222222', for: null }, // remove for
{ from: 'server', msg: null, for: 'data/64.....' } // remove msg
];
console.log(message)
You could filter the array and map the rest.
const
fork = [{ from: 'client', msg: null, for: null }, { from: 'client', msg: '2222222222222', for: null }, { from: 'server', msg: 'wqqqqqqqqqqqq', for: 'data/64.....' }];
result = fork
.filter(o => ['msg', 'for'].some(k => o[k] !== null))
.map(o => Object.fromEntries(Object
.entries(o)
.filter(([, v]) => v !== null)
));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const fork = [
{ from: 'client', msg: null, for: null },
{ from: 'client', msg: '2222222222222', for: null },
{ from: 'server', msg: 'wqqqqqqqqqqqq', for: 'data/64.....' }
];
const result = fork.map(item => {
if (Object.values(item).filter(i => i === null).length > 1) return null
const obj = {...item}
for (const key in obj) {
if (obj[key] === null) {
delete obj[key]
}
}
return obj
}).filter(item => item !== null)
console.log(result)
Using reduce method
const fork = [
{ from: "client", msg: null, for: null },
{ from: "client", msg: "2222222222222", for: null },
{ from: "server", msg: "wqqqqqqqqqqqq", for: "data/64....." },
];
const res = fork.reduce((acc, curr) => {
const obj = { from: curr.from };
["msg", "for"].forEach((key) => (curr[key] !== null && (obj[key] = curr[key])));
return Object.keys(obj).length > 1 ? acc.concat(obj) : acc;
}, []);
console.log(res);
Related
Hi everyone I have following code
I want to show my DefaultValue nested objects like this
["DefaultValue", "DefaultValue", "DefaultValue","DefaultValue","DefaultValue","DefaultValue"]
I have following data from backend:
const data = [
{
id: 243,
Name: "test",
type: "checkbox",
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: ["a"]
}
}
}
}
}
}
];
So I am trying to do following, but it's not works, its says like Cannot convert undefined or null to object
const innerObject = o => {
return Object.keys(o).reduce(function (r, k) {
return typeof o[k] === 'object' ? innerObject(o[k]) : ((r[k] = o[k]), r);
}, {});
};
Please help me to resolve this problem.
you can try this:
const data = [
{
id: 243,
Name: "test",
type: "checkbox",
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: {
DefaultValue: ["a"]
}
}
}
}
}
}
];
const makeArr = (obj, arr = []) =>{
if(typeof obj === 'object' && obj !== null){
arr.push('DefaultValue');
return makeArr(obj.DefaultValue, arr)
}else{
return arr;
}
}
console.log(makeArr(data[0].DefaultValue))
I got this type of object:
const obj = {
group: {
data: {
data: [
{
id: null,
value: 'someValue',
data: 'someData'
}
]
}
}
};
I need to edit this object so whenever null is in the property value,
it would be replaced with some string.
Meaning if the replacement string will be 'someId',
the expected outcome is:
const obj = {
group: {
data: {
data: [
{
id: 'someId',
value: 'someValue',
data: 'someData'
}
]
}
}
};
Closest I found were this and this but didn't manage to manipulate the solutions there to what i need.
How should I do it?
Probably running into issues with the array values. Pass in the index of the array to modify. In this case [0]
obj.group.data.data[0].id = "someId"
EDIT
This will update all null values of id inside the data array:
obj.group.data.data.forEach(o => {
if (o.id === null) {
o.id = "someId"
}
})
Another EDIT
Here is an algorithm to recursively check all deeply nested values in an object. It will compile an array of object paths where null values live. There is an included helper method to find and update the value of the object at the given path in the array. There is a demonstration of the program in the console.
const object = {
group: {
data: {
data: [
{
id: null,
value: "foo",
data: [null, "bar", [null, { stuff: null }]]
},
{
id: null,
value: null,
data: {
bar: [null]
}
},
{
id: null,
value: "foo",
data: null
},
{
id: 4,
value: "foo",
data: "bar"
},
{
id: 4,
value: "stuff",
data: null
}
]
},
attributes: null,
errors: ["stuff", null]
}
}
const inspectProperty = (key, obj, path = "") => {
if (typeof obj[key] === "object") {
if (obj[key] instanceof Array) {
return analyzeArray(obj[key], `${path ? path + "." : ""}${key}`);
}
return analyzeObj(obj[key], `${path ? path + "." : ""}${key}`);
}
return [];
};
const analyzeKey = (obj, key, path = "") => {
if (obj[key] === null) return [`${path ? path + "." : ""}${key}`];
return inspectProperty(key, obj, path).reduce((a, k) => [...a, ...k], []);
};
const analyzeObj = (obj, path = "") => {
return Object.keys(obj).map((item) => analyzeKey(obj, item, path));
};
const analyzeArray = (array, path) => {
return array.map((item, i) => analyzeKey(array, i, path));
};
const updateNullValue = (path, value) => {
let p = path.split(".");
p.reduce((accum, iter, i) => {
if (i === p.length - 1) {
accum[iter] = value;
return object;
}
return accum[iter];
}, object);
};
let nullValues = analyzeObj(object)[0]
console.log(nullValues)
nullValues.forEach((nullVal, i) => {
updateNullValue(nullVal, "hello-" + i)
})
console.log(object)
Given an object or array, I want to be able to determine if the path exists or not.
Given - Example 1
const spath = "data/message";
const body = {
data: {
school: 'yaba',
age: 'tolu',
message: 'true'
},
time: 'UTC',
class: 'Finals'
}
it should return true because message can be found in body.data.message else return false.
Given - Example 2
const spath = "data/message/details/lastGreeting";
const body = {
data: {
school: 'yaba',
age: 'tolu',
message: {
content: 'now',
details: {
lastGreeting: true
}
}
},
time: 'UTC',
class: 'Finals'
}
it should return true because lastGreeting can be found in body.data.message.details.lastGreeting else return false.
The other condition is when the body consists of an array
Given - Example 3
const spath = "data/area/NY";
const body = {
data: {
school: 'yaba',
age: 'tolu',
names : ['darious'],
area: [{
NY: true,
BG: true
]]
message: {
content: 'now',
details: {
lastGreeting: true
}
}
},
time: 'UTC',
class: 'Finals'
}
it should return true because NY can be found in body.data.area[0].NY else return false.
This is the solution I came up with
const findPathInObject = (data, path, n) => {
console.log('entered')
console.log(data, path)
if(!data){
return false
}
let spath = path.split('/');
for(let i = 0; i<n; i++){
let lastIndex = spath.length - 1;
if(spath[i] in data && spath[i] === spath[lastIndex]){
return true
}
const currentIndex = spath[i];
// spath.splice(currentIndex, 1);
return findPathInObject(data[spath[currentIndex]], spath[i+1], spath.length)
}
return false
}
console.log(findPathInObject(body, spath, 3))
You could take some checks in advance and check if path is an empry string, then exit with true.
By having an array, you could exit early by checking the elements of the array with the actual path by omitting the indices.
For the final check of a key, you could check the existence of it and return the result of the recursove call with the rest path or return false, if the key is not in the object.
const
findPathInObject = (data, path) => {
if (!path) return true;
if (!data || typeof data !== 'object') return false;
if (Array.isArray(data)) return data.some(d => findPathInObject(d, path));
const
spath = path.split('/'),
key = spath.shift();
return key in data
? findPathInObject(data[key], spath.join('/'))
: false;
};
console.log(findPathInObject({ data: { school: 'yaba', age: 'tolu', message: 'true' }, time: 'UTC', class: 'Finals' }, "data/message", 3)); // true
console.log(findPathInObject({ data: { school: 'yaba', age: 'tolu', message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' }, "data/message/details/lastGreeting", 3)); // true
console.log(findPathInObject({ data: { school: 'yaba', age: 'tolu', names: ['darious'], area: [{ NY: true, BG: true }], message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' }, "data/area/NY", 3)); // true
find
For this answer, I'm going to provide a tree with varying degrees of nesting of objects and arrays -
const tree =
{ data:
{ school: "yaba", age: "tolu", message: "foo" }
, classes:
[ { name: "math" }, { name: "science" } ]
, deep:
[ { example:
[ { nested: "hello" }
, { nested: "world" }
]
}
]
}
Generators are a fantastic fit for this type of problem. Starting with a generic find which yields all possible results for a particular path -
function find (data, path)
{ function* loop (t, [k, ...more])
{ if (t == null) return
if (k == null) yield t
else switch (t?.constructor)
{ case Object:
yield *loop(t[k], more)
break
case Array:
for (const v of t)
yield *loop(v, [k, ...more])
break
}
}
return loop(data, path.split("/"))
}
Array.from(find(tree, "classes/name"))
Array.from(find(tree, "deep/example/nested"))
Array.from(find(tree, "x/y/z"))
[ "math", "science" ]
[ "hello", "world" ]
[]
find1
If you want a function that returns one (the first) result, we can easily write find1. This is particularly efficient because generators are pauseable/cancellable. After the first result is found, the generator will stop searching for additional results -
function find1 (data, path)
{ for (const result of find(data, path))
return result
}
find1(tree, "data/school")
find1(tree, "classes")
find1(tree, "classes/name")
find1(tree, "deep/example/nested")
find1(tree, "x/y/z")
"yaba"
[ { name: "math" }, { name: "science" } ]
"math"
"hello"
undefined
exists
If you care to check whether a particular path exists, we can write exists as a simple specialisation of find1 -
const exists = (data, path) =>
find1(data, path) !== undefined
exists(tree, "data/school")
exists(tree, "classes")
exists(tree, "deep/example/nested")
exists(tree, "x/y/z")
true
true
true
false
demo
Expand the snippet below to verify the results in your own browser -
function find (data, path)
{ function* loop (t, [k, ...more])
{ if (t == null) return
if (k == null) yield t
else switch (t?.constructor)
{ case Object:
yield *loop(t[k], more)
break
case Array:
for (const v of t)
yield *loop(v, [k, ...more])
break
}
}
return loop(data, path.split("/"))
}
function find1 (data, path)
{ for (const result of find(data, path))
return result
}
const tree =
{ data:
{ school: "yaba", age: "tolu", message: "foo" }
, classes:
[ { name: "math" }, { name: "science" } ]
, deep:
[ { example:
[ { nested: "hello" }
, { nested: "world" }
]
}
]
}
console.log("find1")
console.log(find1(tree, "data/school"))
console.log(find1(tree, "classes"))
console.log(find1(tree, "classes/name"))
console.log(find1(tree, "deep/example/nested"))
console.log(find1(tree, "x/y/z"))
console.log("find")
console.log(Array.from(find(tree, "classes/name")))
console.log(Array.from(find(tree, "deep/example/nested")))
console.log(Array.from(find(tree, "x/y/z")))
Strictly spoken, body.data.area[0].NY is not in the path of body, sorry. body.data.area is in the path. For the object without body.data.area as array here's a solution. If you want to include objects within arrays as part of an objects path, the solution will be more complex
const spath = "data/area/NY";
const spath2 = "data/message/details/lastGreeting";
const notPath = "data/message/details/firstGreeting";
const body = {
data: {
school: 'yaba',
age: 'tolu',
names : ['darious'],
area: {
NY: true,
BG: true
},
message: {
content: 'now',
details: {
lastGreeting: true
}
}
},
time: 'UTC',
class: 'Finals'
};
console.log(`${spath} exists? ${ exists(body, spath) && `yep` || `nope`}`);
console.log(`${spath2} exists? ${ exists(body, spath2) && `yep` || `nope`}`);
console.log(`${notPath} exists? ${ exists(body, notPath) && `yep` || `nope`}`);
function exists(obj, path) {
const pathIterable = path.split("/");
while (pathIterable.length) {
const current = pathIterable.shift();
// no path left and exists: true
if (pathIterable.length < 1 && current in obj) { return true; }
// up to now exists, path continues: recurse
if (current in obj) { return exists(obj[current], pathIterable.join("/")); }
}
// no solution found: false
return false;
}
You can check this solution. Will also check for array of objects.
const body = {
data: {
school: 'yaba',
age: 'tolu',
message: {
content: 'now',
details: {
lastGreeting: true,
},
},
area: [
{
NY: true,
BG: true,
},
],
},
time: 'UTC',
class: 'Finals',
};
const spath1 = 'data/message';
const spath2 = 'data/message/details/lastGreeting';
const spath3 = 'data/area/NY';
const spath4 = 'data/area/NY/Test';
console.log(`${spath1}: `, isPathExists(body, spath1.split('/'), 0));
console.log(`${spath2}: `, isPathExists(body, spath2.split('/'), 0));
console.log(`${spath3}: `, isPathExists(body, spath3.split('/'), 0));
console.log(`${spath4}: `, isPathExists(body, spath4.split('/'), 0));
function isPathExists(data, pathArr, i) {
const key = pathArr[i];
if (Array.isArray(data)) {
for (let value of data) {
if (isObject(value)) return isPathExists(value, pathArr, i);
}
} else if (data.hasOwnProperty(key)) {
if (key === pathArr[pathArr.length - 1]) return true;
return isPathExists(data[key], pathArr, i + 1);
} else return false;
return true;
}
function isObject(a) {
return !!a && a.constructor === Object;
}
With help of a colleague we eventually came up with something simple and easy to comprehend that really suits our needs. The answer with the yield implementation solves the problem but we were looking for something someone can read and easily understand in the codebase. We wanted to be able to check if the path exists in the object as well as get the value.
So we added a third param called returnValue - by default it will always return the value. If we don't want it to do that, we can set the value of the return value to false and the function will check if the path am looking exists, if it does, it will return true else return false
This is what we finally came up with
const find = (path, data) => {
if (Array.isArray(data)) {
data = data[0];
}
for (const item in data) {
if (item === path) {
return data[item];
}
}
return null;
};
const findPath = (fullPath, fullData, returnValue = true) => {
const pathArray = fullPath.split('/');
let findResult = fullData;
for (const pathItem of pathArray) {
findResult = find(pathItem, findResult);
if (!findResult) {
if (!returnValue) return false;
return null;
}
}
if (!returnValue) return true;
return findResult;
};
const body = {
name: 'mike',
email: 1,
data: {
school: [
{
testing: 123
}
]
}
}
console.log(findPath('data/school/testing', body))
Here is a clean, iterative solution using object-scan
.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/object-scan#18.1.2/lib/index.min.js';
const data1 = { data: { school: 'yaba', age: 'tolu', message: 'true' }, time: 'UTC', class: 'Finals' };
const data2 = { data: { school: 'yaba', age: 'tolu', message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' };
const data3 = { data: { school: 'yaba', age: 'tolu', names: ['darious'], area: [{ NY: true, BG: true }], message: { content: 'now', details: { lastGreeting: true } } }, time: 'UTC', class: 'Finals' };
const path1 = 'data/message';
const path2 = 'data/message/details/lastGreeting';
const path3 = 'data/area/NY';
const exists = (data, n) => objectScan([n.replace(/\//g, '.')], {
useArraySelector: false,
rtn: 'bool',
abort: true
})(data);
console.log(exists(data1, path1));
// => true
console.log(exists(data2, path2));
// => true
console.log(exists(data3, path3));
// => true
</script>
Disclaimer: I'm the author of object-scan
I have next array of objects:
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
What I need is to filter out elements which is empty, null or undefined and convert it to one object pameters:
{
givenName: 'firstName',
familyName: 'lastName',
'custom:data': 'blabla'
}
You could filter the array by looking to the values. This approach assumes, that only one key/value pair is available.
const
fields = [{ givenName: 'firstName' }, { familyName: 'lastName' }, { 'custom:data': 'blabla' }, { 'custom:data2': '' }, { 'custom:data3': null }],
result = Object.assign({}, ...fields.filter(o => {
const [v] = Object.values(o);
return v || v === 0 || v === false;
}));
console.log(result);
How to check whether a value is empty?
Most people would go about this with a truthy check:
const empty = x => x ? false : true;
empty(null); //=> true
empty(undefined); //=> true
empty(''); //=> true
But that's always going to exclude things you perhaps didn't intend to exclude:
empty(0); //=> true
empty(false); //=> true
empty(NaN); //=> true
Admittedly NaN could be seen as the "empty" value of its type but for the sake of your question and educational purpose we'll say it's not.
The "workaround" is often something like that:
const empty = x => (x || x === 0 || x === false || Number.isNaN(x)) ? false : true;
However this doesn't need to be more complicated than this:
const empty = x => x == null || x === '' ? true : false;
Checking for either undefined or null is one example where not using triple equality makes sense:
null == undefined;
// true
null === undefined;
// false
See Google JavaScript Style Guide.
If you need to exclude null, undefined and '' please don't rely on clever shorthand tricks and just be explicit about it. Type checking should be a straightforward job (YMMV) and not a show-off contest. Future you and your team mates will thank you.
As for your question, I'd suggest this:
Merge everything you've got with Object.assign:
Object.assign({}, {a:1}, {b:2}, {c:2});
// {a: 1, b: 2, c: 3}
Then deconstruct it into pairs, exclude those whose value is empty, then reconstruct the object from what's left:
const merge = xs =>
Object.fromEntries(
Object.entries(
Object.assign({}, ...xs))
.filter(([_, v]) =>
v != null && v !== ''));
console.log(merge(fields));
<script>
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
</script>
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
res = fields.reduce((acc, cur) => {
if (cur[Object.keys(cur)[0]]) {
acc = { ...acc, ...cur }
}
return acc
}, {})
console.log(res)
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
const result = fields.reduce( ( acc, field ) => {
Object.keys( field ).forEach( ( key ) => {
if( field[key] ) {
acc[key] = field[key];
}
} )
return acc;
}, {} )
console.log(result)
You could use reduce and forEach and check if value of each property is falsy or not.
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
const result = fields.reduce((r, e) => {
Object.entries(e).forEach(([k, v]) => {
if (v || [false, 0].includes(v)) r[k] = v
})
return r
}, {})
console.log(result)
Use Array.prototype.filter() method to filter out the empty, null or undefined objects. Then using Array.prototype.map() make a key-value pair array. At last, use Object.fromEntries() method to transform it to a single object.
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
const ret = Object.fromEntries(
fields
.filter((x) => {
const value = Object.values(x)[0];
return value || value === false || value === 0 || Object.is(value, NaN);
})
.map((x) => [Object.keys(x)[0], Object.values(x)[0]])
);
console.log(ret);
It might help you.
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
let item = {};
for ( let i = 0; i < fields.length; i++ ){
for (const [key, value] of Object.entries(fields[i])) {
if ( value !== null && value !== '' )
item [key] = value;
}
}
console.log(item);
Works with one key, simple modification can work on n keys.
const fields = [
{ givenName: "firstName" },
{ familyName: "lastName" },
{ "custom:data": "blabla" },
{ "custom:data2": "" },
{ "custom:data3": null },
];
const reduced = fields
.filter((f) => {
const key = Object.keys(f)[0];
return f[key] === "" || f[key] === null || f[key] === undefined
? false
: true;
})
.reduce((acc, curr) => {
const key = Object.keys(curr)[0];
acc[key] = curr[key];
return acc;
}, {});
console.log(reduced);
I am running a Node.js project. This is my response which I get from an API:
assets: [
{
id: '93b1d990-4110-11eb-b553-0b6473e34c34',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.7416_cliperago_test_image_01.jpg',
type: 'institute_logo',
},
{
id: '93bf3390-4110-11eb-bee1-bf5b057e2905',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.8143_cliperago_video_demo_02.mp4',
type: 'pre_roll_video',
},
{
id: '93d3b550-4110-11eb-979a-4ddd3ec99c0f',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.9016_cliperago_video_demo_03.mp4',
type: 'out_roll_video',
}
]
I want to get the name of the object with type out_roll_video
This is my code
axios.get('https://testrider/api/v1/student/bb533310/group_assets')
.then(response => {
let a = response.data.data.clips_assets
console.log(a);
// console.log(response.data.data.clips_assets.assets);
a.forEach(obj => {
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key}`);
if(`${value}` == 'out_roll_video'){
console.log(` ${key}`);
if(`${key}` == 'name'){
console.log(` ${value}`);
}
}
});
console.log('-------------------');
})
.catch(error => {
console.log("failed");
});
You can use .find to get a specific name according to a condition. If you want to get more than one, you can use .filter to get the objects matching, and then get the names using .map:
const assets= [
{
id: '93b1d990-4110-11eb-b553-0b6473e34c34',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.7416_cliperago_test_image_01.jpg',
type: 'institute_logo',
},
{
id: '93bf3390-4110-11eb-bee1-bf5b057e2905',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.8143_cliperago_video_demo_02.mp4',
type: 'pre_roll_video',
},
{
id: '93d3b550-4110-11eb-979a-4ddd3ec99c0f',
clip_setting_id: '93a6e4d0-4110-11eb-8a7e-09b69bef45f7',
name: '6c9cdb10-4110-11eb-bc48-096965779f51_1608282501.9016_cliperago_video_demo_03.mp4',
type: 'out_roll_video',
}
];
const { name } = assets.find(e => e.type === 'out_roll_video') || {};
console.log(name);
const names = assets
.filter(e => e.type === 'out_roll_video' || e.type === 'pre_roll_video')
.map(e => e.name);
console.log(names);