So I have an array of objects and I want to get the object with the key "Z".
Obviously I can just loop through the array and check each key one by one and grab the one which matches, but I was thinking that there is probably a better way than my current approach:
for (var i = 0; i < data.length; i++) {
if (Object.keys(data[i]).toString() == "z") {
return data[i].z;
break;
}
}
My data:
"data": [
{ "X": { "foo": "bar1" } },
{ "Y": { "foo": "bar2" } },
{ "Z": { "foo": "bar3" } }
]
Desired Output:
{
"foo": "bar3"
}
Instead of an array of objects, you could replace it with an object:
"data": {
"X": { "foo": "bar1" },
"Y": { "foo": "bar2" },
"Z": { "foo": "bar3" }
}
And then access your object like so:
data['Z']
as you can see, much neater.
I'm guessing you used an array originally for easy appending and so on, but it's just as easy with an object:
data['A'] = { "foo": "bar4" };
will create key "A" in data, and you can still loop through your object using for (... in ...) loops, i.e:
for (key in data) {
console.log(data[key].foo);
}
should print
bar1
bar2
bar3
bar4
Using lodash, you could do something like:
var collection = [
{ X: { foo: 'bar1' } },
{ Y: { foo: 'bar2' } },
{ Z: { foo: 'bar3' } }
];
_(collection)
.chain()
.find(_.ary(_.partialRight(_.has, 'Z'), 1))
.values()
.first()
.value()
// → { foo: 'bar3' }
An outline of what this chain is doing:
chain(): Enables explicit chaining. Otherwise, find() will return an unwrapped object, and we still have actions to perform.
find(): We pass in a callback that checks for the existence of the Z key. The callback itself is constructed using higher-order function utilities:
ary(): Restricts the number of arguments passed to the function. We do this here because find() passes arguments that we don't care about to our callback.
partialRight(): Provides the 'Z' argument to the has() function. This is the rightmost argument, meaning that each item in the collection is passed as the first argument.
has(): Returns true if the Z key exists on an object.
values(): We don't care about the object key, only it's values.
first(): The values() function returns a collection, but we only want the first item in it. There should only ever be one item in the collection.
Related
I have the following json example:
{
"MyTest:": [{
"main": {
"name": "Hello"
},
"test2": {
"test3": {
"test4": "World"
},
"test5": 5
}
},
{
"main": {
"name": "Hola"
},
"test6": [{
"name": "one"
},
{
"name": "two"
}
]
}
]
}
I'm trying to convert it to an array of arrays with key-values
[[main.name: "Hello",test2.test3.test4: "World", test2.test5: 5] ,
[main.name = "Hola", test6.name: "one", test6.name: "two"] ];
Looking for some function like "is leaf" - so I will know that is the value.
Any advise for deep iteration will be very appriciated.
The flattenObject() function returns an object of one level, with the keys built from all sub keys. The recursive function checks if the current value is an object. If it is, it iterates the object with _.flatMap() and calls itself on each property with the keys collected so far. If the value is not an object, it returns an object with a single property (the joined keys), and the value.
It then merges the array of { key: value } objects to a single object.
const flattenObject = val => {
const inner = (val, keys = []) =>
_.isObject(val) ? // if it's an object or array
_.flatMap(val, (v, k) => inner(v, [...keys, k])) // iterate it and call fn with the value and the collected keys
:
{ [keys.join('.')]: val } // return the joined keys with the value
return _.merge({}, ...inner(val))
}
const obj = {"MyTest":[{"main":{"name":"Hello"},"test2":{"test3":{"test4":"World"},"test5":5}},{"main":{"name":"Hola"},"test6":[{"name":"one"},{"name":"two"}]}]}
const result = obj.MyTest.map(flattenObject)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
I'm working in wso2 carbon dashboard. My table is containing 2 fields (Name and Number). I Have duplicate name in the objects but with different number. I want unique name with addition of numbers.
[
{
"Name":"Abc",
"number":2
},
{
"Name":"bcd",
"number":3
},
{
"Name":"Abc",
"number":5
}
]
expected output
[
{
"name":"Abc",
"Number":7
},
{
"name":"bcd",
"Number":3
}
]
I'm using java script to achieve such task. please help me
Use Array#reduce method with a reference object.
var data = [{
"Name": "Abc",
"number": 2
}, {
"Name": "bcd",
"number": 3
}, {
"Name": "Abc",
"number": 5
}];
// object for index reference
var ref = {};
// iterate and generate the array
var res = data.reduce(function(arr, o) {
// check index already defined
if (!(o.Name in ref)) {
// if not defined then define index
ref[o.Name] = arr.length;
// and push the element
// you can also use
// arr.push({Name:o.Name, number:o.number});
arr.push(Object.assign({}, o));
} else {
// if index already defined update the number
arr[ref[o.Name]].number += o.number;
}
// return the array reference
return arr;
// set initial value as empty array
}, []);
console.log(res);
Since JSON can not perfom functions i need to eval a JSON string by a key flag in the JSON object. I want to mutate the JSON data when it's in Object form.
I can't find a function/method online that can give me the full path to key based on a known key pattern.
Given this data:
{
"js:bindto": "#chart-1", // note the key here 'js:'
"point": {
"r": 5
},
"data": {
"x": "x",
"xFormat": "%Y",
"columns": [
...
],
"colors": {
"company": "#ed1b34",
"trendline": "#ffffff"
}
},
"legend": {
"show": false
},
"axis": {
"x": {
"padding": {
"left": 0
},
"type": "timeseries",
"tick": {
"format": "%Y",
"outer": false
}
},
"y": {
"tick": {
"outer": false,
"js:format": "d3.format(\"$\")" // note the key here 'js:'
}
}
},
"grid": {
"lines": {
"front": false
},
"y": {
"lines": [...]
}
}
}
The flags are keys beginning with js:.
If I look up js:format, I'd expect it's path to be something like: /js:bindto and /axis/y/tick/js:format. Open to suggestions.
In context:
mutateGraphData<T>(data:T):T {
// data here is a parsed JSON string. ( an object as shown above )
let jsonKeys = this.findKeysInJSON(JSON.stringify(data), "js:");
// jsonKeys = ["js:bindto","js:format"]
jsonKeys.map((key:string) => {
// find the key in data, then modify the value. (stuck here)
// i need the full path to the key so i can change the data property that has the key in question
});
});
return data;
}
findKeysInJSON<T>(jsonString:string, key:string):Array<T> {
let keys = [];
if (Boolean(~(jsonString.indexOf(`"${key}`)))) {
let regEx = new RegExp(key + "(\\w|\\-)+", "g");
keys = jsonString.match(regEx);
}
return keys;
}
I have been around a few npm packages:
object search no wild card lookaround
dotty looks good, but fails with search: "*.js:format"
https://github.com/capaj/object-resolve-path - needs full path to key. I don't know that in advance.
I have looked at
JavaScript recursive search in JSON object
Find property by name in a deep object
DefiantJS ( no npm module )
https://gist.github.com/iwek/3924925
Nothing have I seen can return the full path to the key in question so I can modify it, nor work directly on the object itself so I can change its properties.
You could go with Ramda. It has built in functions that will allow you to map over an object and modify parts of the object in a completely immutable way.
Ramda offers R.lensPath that will allow you to dig into the object, and modify it as needed. It doesn't follow the pattern you want, but we can quickly patch it with the lensify function.
It is using the R.set function to set the node to the passed in value, and creating a pipeline that will run all the operations on the passed in object.
You can do some really cool stuff with ramda and lenses. Checkout evilsoft on livecoding.tv for a really good overview.
const obj={"js:bindto":"#chart-1",point:{r:5},data:{x:"x",xFormat:"%Y",columns:[],colors:{company:"#ed1b34",trendline:"#ffffff"}},legend:{show:!1},axis:{x:{padding:{left:0},type:"timeseries",tick:{format:"%Y",outer:!1}},y:{tick:{outer:!1,"js:format":'d3.format("$")'}}},grid:{lines:{front:!1},y:{lines:[]}}}
const lensify = path => R.lensPath(path.split('/'))
// create the property accessors split by /
const bindToLens = lensify('js:bindto')
const formatLens = lensify('axis/y/tick/js:format')
const modifyObj = R.pipe(
R.set(bindToLens, 'dis be bind'),
R.set(formatLens, 'I been here')
)
console.log(modifyObj(obj))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>
After doing a look around I modified the answer from Javascript/JSON get path to given subnode? #adam-rackis
function search(obj, target, path = "") {
for (var k in obj) {
if (obj.hasOwnProperty(k))
if (k === target)
return path + "." + k;
else if (typeof obj[k] === "object") {
var result = search( obj[k], target, path + "." + k );
if (result){
return result;
}
}
}
return false;
}
search(data,"js:format").slice(1); // returns: axis.y.tick.js:format
search(data,"js:bindto").slice(1); // returns: js:bindto
Now I can use dotty or object-resolve-path or plain ole split('.') to resolve the path to the object.
With object-scan this should be a one liner. We've been using it a lot for data processing and it's powerful once you wrap your head around it. Here is how you could use it
// const objectScan = require('object-scan');
const find = (data) => objectScan(['**.js:*'], { joined: true })(data);
const data = { 'js:bindto': '#chart-1', point: { r: 5 }, data: { x: 'x', xFormat: '%Y', columns: [], colors: { company: '#ed1b34', trendline: '#ffffff' } }, legend: { show: false }, axis: { x: { padding: { left: 0 }, type: 'timeseries', tick: { format: '%Y', outer: false } }, y: { tick: { outer: false, 'js:format': 'd3.format("$")' } } }, grid: { lines: { front: false }, y: { lines: [] } } };
console.log(find(data));
// => [ 'axis.y.tick.js:format', 'js:bindto' ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
I am Having the Array of objects. Like this
var result=[{"batchId":123, "licenseId":2345ef34, "name":"xxx"},
{"batchId":345, "licenseId":2345sdf334, "name":"www"},
{"batchId":145, "licenseId":234sdf5666, "name":"eee"},
{"batchId":455, "licenseId":asfd236645 },
{"batchId":678, "name":"aaa"}]
i want to have the array which is contains all the three properties. the Output should be like this.
[{"batchId":123, "licenseId":2345ef34, "name":"xxx"},
{"batchId":345, "licenseId":2345sdf334, "name":"www"},
{"batchId":145, "licenseId":234sdf5666, "name":"eee"}]
can anybody Help me on this
This is simple with the array .filter() method:
var result=[
{"batchId":123, "licenseId":"2345ef34", "name":"xxx"},
{"batchId":345, "licenseId":"2345sdf334", "name":"www"},
{"batchId":145, "licenseId":"234sdf5666", "name":"eee"},
{"batchId":455, "licenseId":"asfd236645" },
{"batchId":678, "name":"aaa"}
];
var filtered = result.filter(function(v) {
return "batchId" in v && "licenseId" in v && "name" in v;
});
console.log(filtered);
The function you pass to .filter() is called for each element in the array. Each element for which you return a truthy value will be included in the resulting array.
In the code above I simply test if all three of those specific properties are present, although there are other tests you could use that would get the same result for that data:
var result=[ {"batchId":123, "licenseId":"2345ef34", "name":"xxx"}, {"batchId":345, "licenseId":"2345sdf334", "name":"www"}, {"batchId":145, "licenseId":"234sdf5666", "name":"eee"}, {"batchId":455, "licenseId":"asfd236645" }, {"batchId":678, "name":"aaa"} ];
var filtered = result.filter(function(v) {
return Object.keys(v).length === 3;
});
console.log(filtered);
Note that you need to put your licenseId values in quotes, because they seem to be string values.
var result = [{
"batchId": 123,
"licenseId": '2345ef34',
"name": "xxx"
}, {
"batchId": 345,
"licenseId": '2345sdf334',
"name": "www"
}, {
"batchId": 145,
"licenseId": '234sdf5666',
"name": "eee"
}, {
"batchId": 455,
"licenseId": 'asfd236645'
}, {
"batchId": 678,
"name": "aaa"
}];
function hasProperties(object) {
return object.hasOwnProperty('batchId') && object.hasOwnProperty('licenseId') && object.hasOwnProperty('name')
}
result.filter(e => hasProperties(e));
I'm serializing objects to JSON strings with JavaScript,
I noticed only enumerable object properties get serialized:
var a = Object.create(null,{
x: { writable:true, configurable:true, value: "hello",enumerable:false },
y: { writable:true, configurable:true, value: "hello",enumerable:true }
});
document.write(JSON.stringify(a)); //result is {"y":"hello"}
[pen]
I'm wondering why that is? I've searched through the MDN page, the json2 parser documentation.
I could not find this behavior documented any-where.
I suspect this is the result of using for... in loops that only go through [[enumerable]] properties (at least in the case of json2). This can probably be done with something like Object.getOwnPropertyNames that returns both enumerable, and non-enumerable properties.
That might be problematic to serialize though (due to deserialization).
tl;dr
Why does JSON.stringify only serialize enumerable properties?
Is this behavior documented anywhere?
How can I implement serializing non-enumerable properties myself?
It's specified in the ES5 spec.
If Type(value) is Object, and IsCallable(value) is false
If the [[Class]] internal property of value is "Array" then
Return the result of calling the abstract operation JA with argument value.
Else, return the result of calling the abstract operation JO with argument value.
So, let's look at JO. Here's the relevant section:
Let K be an internal List of Strings consisting of the names of all the own properties of value whose [[Enumerable]] attribute is true. The ordering of the Strings should be the same as that used by the Object.keys standard built-in function.
As #ThiefMaster answered above, it's specified in the spec
however, if you know the names of the non-enumerable properties you like to be serialized ahead of time, you can achieve it by passing a replacer function as the second param to JSON.stringify() (documentation on MDN), like so
var o = {
prop: 'propval',
}
Object.defineProperty(o, 'propHidden', {
value: 'propHiddenVal',
enumerable: false,
writable: true,
configurable: true
});
var s = JSON.stringify(o, (key, val) => {
if (!key) {
// Initially, the replacer function is called with an empty string as key representing the object being stringified. It is then called for each property on the object or array being stringified.
if (typeof val === 'object' && val.hasOwnProperty('propHidden')) {
Object.defineProperty(val, 'propHidden', {
value: val.propHidden,
enumerable: true,
writable: true,
configurable: true
});
}
}
return val;
});
console.log(s);
You can achieve a generic JSON.stringify() that includes non-enumerables with code like below. But first, some notes:
This code has not been thoroughly tested for possible bugs or unexpected behavior; also, it turns functions into basic objects. Treat accordingly.
copyEnumerable() is the function to pay attention to. It does not itself internally call JSON.stringify() (you can do that yourself, with the output object) mainly because recursive calls to JSON.stringify() (due to nested objects) will yield uglier, unwanted results.
I'm only checking for object and function types to be treated specially; I believe that these are the only types which need to be handled specially... Note that JS Arrays ([]) fall into this category (objects), as well as classes (functions), and null does not. Since null is of type object, I included the !! check.
The stopRecursiveCopy Set is just to make sure it's not doing infinite recursion.
I'm using a trick with the 2 extra parameters to JSON.stringify() calls to make it output something formatted prettier, for easy reading.
The code is in an easy format to try out and tweak in the console:
{
o = {};
d = {};
Object.defineProperties(o, {
a: {
value: 5,
enumerable: false
},
b: {
value: "test",
enumerable: false
},
c: {
value: {},
enumerable: false
}
});
Object.defineProperty(o.c, "d", {
value: 7,
enumerable: false
});
//The code to use (after careful consideration!).
function isObject(testMe) {
return ((typeof(testMe) === "object" && !!testMe) ||
typeof(testMe) === "function");
}
let stopRecursiveCopy = new Set();
function copyEnumerable(obj) {
if (!isObject(obj)) {
return obj;
}
let enumerableCopy = {};
for (let key of Object.getOwnPropertyNames(obj)) {
if (isObject(obj[key])) {
if (!stopRecursiveCopy.has(obj[key])) {
stopRecursiveCopy.add(obj[key]);
enumerableCopy[key] = copyEnumerable(obj[key]);
stopRecursiveCopy.delete(obj[key]);
}
} else {
enumerableCopy[key] = obj[key];
}
}
return enumerableCopy;
}
console.log(JSON.stringify(copyEnumerable(o), null, " "));
Object.defineProperty(copyEnumerable, "test", {
value: 10,
enumerable: false
});
console.log(JSON.stringify(copyEnumerable(copyEnumerable), null, " "));
Object.defineProperty(o, "f", {
value: copyEnumerable,
enumerable: false
});
console.log(JSON.stringify(copyEnumerable(o), null, " "));
}
which outputs:
//o
{
"a": 5,
"b": "test",
"c": {
"d": 7
}
}
//copyEnumerable itself
{
"test": 10,
"prototype": {
"constructor": {
"test": 10,
"length": 1,
"name": "copyEnumerable"
}
},
"length": 1,
"name": "copyEnumerable"
}
//o again, but with `f: copyEnumerable` added
{
"a": 5,
"b": "test",
"c": {
"d": 7
},
"f": {
"test": 10,
"prototype": {},
"length": 1,
"name": "copyEnumerable"
}
}
Pertinent links:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
https://stackoverflow.com/a/14706877/1599699
https://stackoverflow.com/a/18538851/1599699