Related
I created a submenu from the first keys of the "res" data before the dot. In this submenu, I showed the keys with the same name in the "res" data only once. ie: "user", "department" and "project" appear as just a submenu item. But when I click on them, other keys with the same name do not appear once. For example: When I click on the "project" item, 5 "type" submenus are opened. And I need to do the same for the "id" key, too. How can I solve this?
var res = {
"user.name.firstname": "firstname",
"user.name.lastname": "lastname",
"department.id1": 1,
"department.id2": 2,
"project.name": "project",
"project.type.name": "project1",
"project.type.id.first3": "321",
"project.type.id.last3" : "789",
"project.type.id.abc": "abc",
}
var myList = []
var myFinalList = []
Object.keys(res).forEach(key => {
var subMenus = key.split(".")
subMenus.forEach(subMenu => {
var newObject = {}
newObject["id"] = subMenu
newObject["content"] = subMenu
myList.push(newObject)
})
for (var i = myList.length - 1; i >= 0; i--) {
if (i - 1 !== -1) {
//console.log(i - 1)
myList[i - 1]["submenu"] = [myList[i]]
}
}
myFinalList.push(myList[0])
//console.log(myList)
myList = []
})
var mySet = new Set()
myFinalList.forEach(obj => {
mySet.add(obj["id"])
})
var veryFinalList = []
mySet.forEach(key => {
var eachKeyObject = myFinalList.filter(sub => sub.id === key)
var sourceObject = eachKeyObject[0]
if(eachKeyObject.length > 1){
for(var i = 1; i < eachKeyObject.length; i++){
sourceObject["submenu"] = sourceObject["submenu"].concat(eachKeyObject[i]["submenu"])
}
}
veryFinalList.push(sourceObject)
console.log(veryFinalList)
})
And my output should be like this:
output= {
"user": {
"name": {
"firstname": "firstname",
"lastname": "lastname",
}
},
"department": {
"id1":1,
"id2":2,
},
"project": {
"name": "project",
"type": {
"name": "project1",
"id": {
"first3": "321",
"last3" : "789",
"abc" : "abc"
}
}
}
}
Here you go:
var res = {
"user.name.firstname": "firstname",
"user.name.lastname": "lastname",
"department.id1": 1,
"department.id2": 2,
"project.name": "project",
"project.type.name": "project1",
"project.type.id.first3": "321",
"project.type.id.last3" : "789",
"project.type.id.abc": "abc",
}
let finalObj = { };
for(let key in res) {
let value = res[key];
let keyArr = key.split('.');
// Magic happens here (https://stackoverflow.com/a/68860247/2284136)
keyArr.reduce((acc, key, i) => {
if (i < keyArr.length - 1) {
if (!acc[key]) acc[key] = {};
}
else {
acc[key] = value;
}
return acc[key];
}, finalObj)
}
console.log(finalObj);
I used this answer for the reduce implementation, which allows us to put data deep into an object path. We just have to split the keys into and array first, like "user.name.firstname" => ["user", "name", "firstname"] and feed that to the reduce function with the value of res["user.name.firstname"].
Regarding your actual question about "converting a method to recursive function", I did try something like that at first, until I realized we don't need to do that here.
Also for a future reference, while recursive functions are great for some specific problems, the rule of thumb in general is to avoid having to use them if you don't absolutely have to. They are hard to read and understand, hard to debug and can lead to all kinds of wacky problems.
And regarding what you had tried to do to solve this. I think it's a great try and that is exactly what a great programmer should do when running into trouble! I love reading code like that because I can see the rabbit hole of frustration you were digging yourself into when trying to solve the problem. While it is indeed greatly frustrating, it also just as great learning experience. I personally think that when you find yourself from the bottom of that hole, still without a solution, that is the best time to ask for help, like you did!
I will only get an object in comparison the id that from is decided from the user with objects of the arrays.
var laptops = [{
"name": "Firefox",
"age": 30,
"id": "ab"
},
{
"name": "Google",
"age": 35,
"id": "cd",
"date": "00.02.1990"
},
{
"name": "Microsoft",
"id": "ef",
"age": 40
}
];
getLaptopByAsin(id: string) {
var laptop = this.laptops.find(
(laptopObject) => {
return laptopObject.name === id;
}
);
return laptop;
}
var enter = "cd";
name = this.getLaptopByAsin(enter).name;
age = this.getLaptopByAsin(enter).age;
date = this.getLaptopByAsin(enter).date;
console.log(name)
console.log(age)
console.log(date)
Your problem is that you filter on name:
this.laptops.find(
(laptopObject) => {
return laptopObject.name === id;
}
);
After fixing your issue and do some refactoring, the code becomes this:
getLaptopByAsin(id: string) {
return this.laptops.find(
laptopObject => laptopObject.id === id
);
}
Some other comments:
Try to use const instead of var more often where possible.
Saving date the way you do, might give you some headache in the future. use some standard instead, like https://en.wikipedia.org/wiki/Epoch
Avoid multiple function calls when they are not needed, save the result from getLaptopByAsin to a variable (const foundLaptop) so you don't need to iterate over the array multiple times. eg:
const foundLaptop = this.getLaptopByAsin('cd');
console.log(foundLaptop.name);
console.log(foundLaptop.age, foundLaptop.date);
I have the following Json
var myjson = [{
"files": [
{
"domain": "d",
"units": [
{
"key": "key1",
"type": "2"
},
{
"key": "key2",
"type": "2"
},
{
"key": "key3",
"type": "2"
}]
},
{
"domain": "d1",
"units": [
{
"key": "key11",
"type": "2"
},
{
"key": "key12",
"type": "2"
},
{
"key": "key13",
"type": "2"
}]
}]
},
{
"files": [
{
"domain": "d",
"units": [
{
......
I want to create an new array from this Json array. The length of array will be the number of "units" in this Json object.
So I need to extract "units" and add some data from parent objects.
units: [{
domain: "",
type: "",
key: ""
}, {
domain: "",
type: "",
key: ""
},
{
domain: "",
type: "",
key: ""
}
....
];
I guess i can probably do something like this:
var res = [];
myjson.forEach(function(row) {
row.files.forEach(function(tfile) {
tfile.units.forEach(function(unit) {
var testEntity = {
domain: tfile.domain,
type : unit.type,
key: unit.key
};
res.push(testEntity);
});
});
});
But it is difficult to read and looks not so good. I was thinking to do something like :
var RESULT = myjson.map(function(row) {
return row.files.map(function(tfile) {
return tfile.units.map(function(unit) {
return {
domain: tfile.domain,
type : unit.type,
key: unit.key
};
});
});
});
But This doesn't work and looks not better . Is there any way to do so it works, maybe in more declarative way. hoped Ramda.js could help.
It there any good approach in general to get data from any Nested json in readable way?
Implementing something like:
nestedjson.findAllOnLastlevel(function(item){
return {
key : item.key,
type: type.key,
domain : item.parent.domain}
});
Or somehow flatten this json so all properties from all parents object are moved to leafs children. myjson.flatten("files.units")
jsbin http://jsbin.com/hiqatutino/edit?css,js,console
Many thanks
The function you can use here is Ramda's R.chain function rather than R.map. You can think of R.chain as a way of mapping over a list with a function that returns another list and then flattens the resulting list of lists together.
// get a list of all files
const listOfFiles =
R.chain(R.prop('files'), myjson)
// a function that we can use to add the domain to each unit
const unitsWithDomain =
(domain, units) => R.map(R.assoc('domain', domain), units)
// take the list of files and add the domain to each of its units
const result =
R.chain(file => unitsWithDomain(file.domain, file.units), listOfFiles)
If you wanted to take it a step further then you could also use R.pipeK which helps with composing functions together which behave like R.chain between each of the given functions.
// this creates a function that accepts the `myjson` list
// then passes the list of files to the second function
// returning the list of units for each file with the domain attached
const process = pipeK(prop('files'),
f => map(assoc('domain', f.domain), f.units))
// giving the `myjson` object produces the same result as above
process(myjson)
Pure JS is very sufficient to produce the result in simple one liners. I wouldn't touch any library just for this job. I have two ways to do it here. First one is a chain of reduce.reduce.map and second one is a chain of reduce.map.map. Here is the code;
var myjson = [{"files":[{"domain":"d","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"d1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]},{"files":[{"domain":"e","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"e1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]}],
units = myjson.reduce((p,c) => c.files.reduce((f,s) => f.concat(s.units.map(e => (e.domain = s.domain,e))) ,p) ,[]);
units2 = myjson.reduce((p,c) => p.concat(...c.files.map(f => f.units.map(e => (e.domain = f.domain,e)))) ,[]);
console.log(units);
console.log(units2);
For ES5 compatibility i would suggest the reduce.reduce.map chain since there is no need for a spread operator. And replace the arrow functions with their conventional counterparts like the one below;
var myjson = [{"files":[{"domain":"d","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"d1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]},{"files":[{"domain":"e","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"e1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]}],
units = myjson.reduce(function(p,c) {
return c.files.reduce(function(f,s) {
return f.concat(s.units.map(function(e){
e.domain = s.domain;
return e;
}));
},p);
},[]);
console.log(units);
Something like this should work. .reduce is a good one for these kind of situations.
const allUnits = myjson.reduce((acc, anonObj) => {
const units = anonObj.files.map(fileObj => {
return fileObj.units.map(unit => {
return {...unit, domain: fileObj.domain})
})
return [...acc, ...units]
}, [])
Note that this relies on both array spreading and object spreading, which are ES6 features not supported by every platform.
If you can't use ES6, here is an ES5 implementation. Not as pretty, but does the same thing:
var allUnits = myjson.reduce(function (acc, anonObj) {
const units = anonObj.files.map(function(fileObj) {
// for each fileObject, return an array of processed unit objects
// with domain property added from fileObj
return fileObj.units.map(function(unit) {
return {
key: unit.key,
type: unit.type,
domain: fileObj.domain
}
})
})
// for each file array, add unit objects from that array to accumulator array
return acc.concat(units)
}, [])
Try this
var myjson = [{
"files": [{
"domain": "d",
"units": [{
"key": "key1",
"type": "2"
}, {
"key": "key2",
"type": "2"
}, {
"key": "key3",
"type": "2"
}]
},
{
"domain": "d1",
"units": [{
"key": "key11",
"type": "2"
}, {
"key": "key12",
"type": "2"
}, {
"key": "key13",
"type": "2"
}]
}
]
}];
//first filter out properties exluding units
var result = [];
myjson.forEach(function(obj){
obj.files.forEach(function(obj2){
result = result.concat(obj2.units.map(function(unit){
unit.domain = obj2.domain;
return unit;
}));
});
});
console.log(result);
I have 2 object arrays. The 1st is an array of managers. The 2nd is an array of selected managers from the 1st array. The difference is I added a property selected: true. I need to now replace the the managers in the first array with selected managers. I am doing this with an AngularJS service I created. I'm sure there is much simpler solution so I'm open to suggestions. JavaScript, jQuery, lodash, LINQ.js are good.
I have a plunker and I have displayed the result I need. Notice the manager that does not have the selected:true property.
plunker
var app = angular.module("mainModule", []);
var MainController = function($scope, service) {
var eventUsers = [
{
"event_Users_ID":1009,"event_ID":11,"user_ID":"15e640c1-a481-4997-96a7-be2d7b3fcabb"
},{
"event_Users_ID":1010,"event_ID":11,"user_ID":"250a19be-e661-4c04-9a50-c84b0e7349b7"
},{
"event_Users_ID":1011,"event_ID":11,"user_ID":"4cada7f0-b961-422d-8cfe-4e96c1fc11dd"
},{
"event_Users_ID":1013,"event_ID":11,"user_ID":"a3125317-5deb-426d-bbb1-06d3bd4ebaa6"
}];
var managers = [
{
"id": "15e640c1-a481-4997-96a7-be2d7b3fcabb",
"fullName": "Kul Srivastva"
},{
"id": "250a19be-e661-4c04-9a50-c84b0e7349b7",
"fullName": "Todd Brothers"
}, {
"id": "4cada7f0-b961-422d-8cfe-4e96c1fc11dd",
"fullName": "Rudy Sanchez"
}, {
"id": "79823c6d-de52-4464-aa7e-a15949fb25fb",
"fullName": "Mike Piehota",
}, {
"id": "a3125317-5deb-426d-bbb1-06d3bd4ebaa6",
"fullName": "Nick Broadhurst"
}];
$scope.result = service.eventUserMatch(eventUsers, managers);
};
function service() {
var vm = this;
vm.eventUserMatch = function (eventUsers, managers) {
var arry = [];
arry = $.map(eventUsers, function (eventUser) {
var manager = $.grep(managers, function (user) {
return user.id === eventUser.user_ID;
})[0];
eventUser.id = manager.id;
eventUser.fullName = manager.fullName;
eventUser.selected = true;
return eventUser;
});
return arry;
};
}
app.controller("MainController", MainController);
app.service('service', service);
You can use Array#map.
// Get all the event user IDs in an array
var eventUserIds = eventUsers.map(e => e.user_ID);
// Iterate over managers
managers = managers.map(e => {
// If manager is present in the event users, `select` it
if (eventUserIds.indexOf(e.id) !== -1) {
e.selected = true;
}
return e;
});
var eventUsers = [{
"event_Users_ID": 1009,
"event_ID": 11,
"user_ID": "15e640c1-a481-4997-96a7-be2d7b3fcabb"
}, {
"event_Users_ID": 1010,
"event_ID": 11,
"user_ID": "250a19be-e661-4c04-9a50-c84b0e7349b7"
}, {
"event_Users_ID": 1011,
"event_ID": 11,
"user_ID": "4cada7f0-b961-422d-8cfe-4e96c1fc11dd"
}, {
"event_Users_ID": 1013,
"event_ID": 11,
"user_ID": "a3125317-5deb-426d-bbb1-06d3bd4ebaa6"
}];
var managers = [{
"id": "15e640c1-a481-4997-96a7-be2d7b3fcabb",
"fullName": "Kul Srivastva"
}, {
"id": "250a19be-e661-4c04-9a50-c84b0e7349b7",
"fullName": "Todd Brothers"
}, {
"id": "4cada7f0-b961-422d-8cfe-4e96c1fc11dd",
"fullName": "Rudy Sanchez"
}, {
"id": "79823c6d-de52-4464-aa7e-a15949fb25fb",
"fullName": "Mike Piehota",
}, {
"id": "a3125317-5deb-426d-bbb1-06d3bd4ebaa6",
"fullName": "Nick Broadhurst"
}];
var eventUserIds = eventUsers.map(e => e.user_ID);
managers = managers.map(e => {
if (eventUserIds.indexOf(e.id) !== -1) {
e.selected = true;
}
return e;
})
console.log(managers);
document.getElementById('result').innerHTML = JSON.stringify(managers, 0, 4);
<pre id="result"></pre>
would this work? Loop through the new array of managers, find the index using lodash of a matching manager object in the old manager array and replace it in the old manager array with the manager from the new manager array if found?
There's probably a more efficient way to write a solution to this but assuming I'm understanding your problem correctly I believe this should work? Can't test as I'm at work currently.
for(var i=0; i < SelectedManagersArray.length; i++){
var index = _.findIndex(OldManagersArray, {id: SelectedManagersArray[i].id, fullName: selectedManagersArray[i].fullName);
//I believe lodash returns a -1 if a matching index isn't found.
if(index !== -1){SelectedManagersArray[index] = OldManagersArray[i]}
}
Simple implementation:
for(var i = 0; i < eventUsers.length; i++) {
for(var j = 0; j < managers.length; j++) {
if(eventUsers[i].user_ID === managers[j].id) {
managers[j].selected = true;
}
}
}
As you said, I do think there may be an easier way to do this.
I advise you to pick a look to SugarJs which is a JavaScript library that extends native objects with so helpful methods.
In your case the doc on Arrays.
For me, it helps a lot dealing with a lot of native JavaScript Object (JSON).
I have a JSON file containing some data I d like to access on my AngularJS website. Now what I want is to get only one object from the array. So I d like for example Item with id 1.
The data looks like this:
{ "results": [
{
"id": 1,
"name": "Test"
},
{
"id": 2,
"name": "Beispiel"
},
{
"id": 3,
"name": "Sample"
}
] }
I'd like to load the data with AngularJS $http functionality like this:
$http.get("data/SampleData.json");
which is working. But how can I now get a specific data object (by id) from the array I get from $http.get ?
Using ES6 solution
For those still reading this answer, if you are using ES6 the find method was added in arrays. So assuming the same collection, the solution'd be:
const foo = { "results": [
{
"id": 12,
"name": "Test"
},
{
"id": 2,
"name": "Beispiel"
},
{
"id": 3,
"name": "Sample"
}
] };
foo.results.find(item => item.id === 2)
I'd totally go for this solution now, as is less tied to angular or any other framework. Pure Javascript.
Angular solution (old solution)
I aimed to solve this problem by doing the following:
$filter('filter')(foo.results, {id: 1})[0];
A use case example:
app.controller('FooCtrl', ['$filter', function($filter) {
var foo = { "results": [
{
"id": 12,
"name": "Test"
},
{
"id": 2,
"name": "Beispiel"
},
{
"id": 3,
"name": "Sample"
}
] };
// We filter the array by id, the result is an array
// so we select the element 0
single_object = $filter('filter')(foo.results, function (d) {return d.id === 2;})[0];
// If you want to see the result, just check the log
console.log(single_object);
}]);
Plunker: http://plnkr.co/edit/5E7FYqNNqDuqFBlyDqRh?p=preview
For anyone looking at this old post, this is the easiest way to do it currently. It only requires an AngularJS $filter. Its like Willemoes answer, but shorter and easier to understand.
{
"results": [
{
"id": 1,
"name": "Test"
},
{
"id": 2,
"name": "Beispiel"
},
{
"id": 3,
"name": "Sample"
}
]
}
var object_by_id = $filter('filter')(foo.results, {id: 2 })[0];
// Returns { id: 2, name: "Beispiel" }
WARNING
As #mpgn says, this doesn't work properly. This will catch more results. Example: when you search 3 this will catch 23 too
personally i use underscore for this kind of stuff... so
a = _.find(results,function(rw){ return rw.id == 2 });
then "a" would be the row that you wanted of your array where the id was equal to 2
I just want to add something to Willemoes answer.
The same code written directly inside the HTML will look like this:
{{(FooController.results | filter : {id: 1})[0].name }}
Assuming that "results" is a variable of your FooController and you want to display the "name" property of the filtered item.
You can use ng-repeat and pick data only if data matches what you are looking for using ng-show
for example:
<div ng-repeat="data in res.results" ng-show="data.id==1">
{{data.name}}
</div>
You can just loop over your array:
var doc = { /* your json */ };
function getById(arr, id) {
for (var d = 0, len = arr.length; d < len; d += 1) {
if (arr[d].id === id) {
return arr[d];
}
}
}
var doc_id_2 = getById(doc.results, 2);
If you don't want to write this messy loops, you can consider using underscore.js or Lo-Dash (example in the latter):
var doc_id_2 = _.filter(doc.results, {id: 2})[0]
If you want the list of items like city on the basis of state id then use
var state_Id = 5;
var items = ($filter('filter')(citylist, {stateId: state_Id }));
Unfortunately (unless I'm mistaken), I think you need to iterate over the results object.
for(var i = 0; i < results.length; i += 1){
var result = results[i];
if(result.id === id){
return result;
}
}
At least this way it will break out of the iteration as soon as it finds the correct matching id.
Why complicate the situation? this is simple write some function like this:
function findBySpecField(data, reqField, value, resField) {
var container = data;
for (var i = 0; i < container.length; i++) {
if (container[i][reqField] == value) {
return(container[i][resField]);
}
}
return '';
}
Use Case:
var data=[{
"id": 502100,
"name": "Bərdə filialı"
},
{
"id": 502122
"name": "10 saylı filialı"
},
{
"id": 503176
"name": "5 sayli filialı"
}]
console.log('Result is '+findBySpecField(data,'id','502100','name'));
output:
Result is Bərdə filialı
The only way to do this is to iterate over the array. Obviously if you are sure that the results are ordered by id you can do a binary search
$scope.olkes = [{'id':11, 'name':'---Zəhmət olmasa seçim edin---'},
{'id':15, 'name':'Türkyə'},
{'id':45, 'name':'Azərbaycan'},
{'id':60, 'name':'Rusya'},
{'id':64, 'name':'Gürcüstan'},
{'id':65, 'name':'Qazaxıstan'}];
<span>{{(olkes | filter: {id:45})[0].name}}</span>
output: Azərbaycan
If you can, design your JSON data structure by making use of the array indexes as IDs. You can even "normalize" your JSON arrays as long as you've no problem making use of the array indexes as "primary key" and "foreign key", something like RDBMS. As such, in future, you can even do something like this:
function getParentById(childID) {
var parentObject = parentArray[childArray[childID].parentID];
return parentObject;
}
This is the solution "By Design". For your case, simply:
var nameToFind = results[idToQuery - 1].name;
Of course, if your ID format is something like "XX-0001" of which its array index is 0, then you can either do some string manipulation to map the ID; or else nothing can be done about that except through the iteration approach.
I know I am too late to answer but it's always better to show up rather than not showing up at all :). ES6 way to get it:
$http.get("data/SampleData.json").then(response => {
let id = 'xyz';
let item = response.data.results.find(result => result.id === id);
console.log(item); //your desired item
});
The simple way to get (one) element from array by id:
The find() method returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.
function isBigEnough(element) {
return element >= 15;
}
var integers = [12, 5, 8, 130, 160, 44];
integers.find(isBigEnough); // 130 only one element - first
you don't need to use filter() and catch first element xx.filter()[0] like in comments above
The same for objects in array
var foo = {
"results" : [{
"id" : 1,
"name" : "Test"
}, {
"id" : 2,
"name" : "Beispiel"
}, {
"id" : 3,
"name" : "Sample"
}
]};
var secondElement = foo.results.find(function(item){
return item.id == 2;
});
var json = JSON.stringify(secondElement);
console.log(json);
Of course if you have multiple id then use filter() method to get all objects.
Cheers
function isBigEnough(element) {
return element >= 15;
}
var integers = [12, 5, 8, 130, 160, 44];
integers.find(isBigEnough); // 130 only one element - first
var foo = {
"results" : [{
"id" : 1,
"name" : "Test"
}, {
"id" : 2,
"name" : "Beispiel"
}, {
"id" : 3,
"name" : "Sample"
}
]};
var secondElement = foo.results.find(function(item){
return item.id == 2;
});
var json = JSON.stringify(secondElement);
console.log(json);
projectDetailsController.controller('ProjectDetailsCtrl', function ($scope, $routeParams, $http) {
$http.get('data/projects.json').success(function(data) {
$scope.projects = data;
console.log(data);
for(var i = 0; i < data.length; i++) {
$scope.project = data[i];
if($scope.project.name === $routeParams.projectName) {
console.log('project-details',$scope.project);
return $scope.project;
}
}
});
});
Not sure if it's really good, but this was helpful for me..
I needed to use $scope to make it work properly.
use $timeout and run a function to search in "results" array
app.controller("Search", function ($scope, $timeout) {
var foo = { "results": [
{
"id": 12,
"name": "Test"
},
{
"id": 2,
"name": "Beispiel"
},
{
"id": 3,
"name": "Sample"
}
] };
$timeout(function () {
for (var i = 0; i < foo.results.length; i++) {
if (foo.results[i].id=== 2) {
$scope.name = foo.results[i].name;
}
}
}, 10);
});
I would iterate over the results array using an angularjs filter like this:
var foundResultObject = getObjectFromResultsList(results, 1);
function getObjectFromResultsList(results, resultIdToRetrieve) {
return $filter('filter')(results, { id: resultIdToRetrieve }, true)[0];
}