This one is a tricky one.
So, lets say I have two JS objects that are fetched via REST call, via two callbacks.
So, we have:
call1() - POST method - parsed JSON to JS object, route: {{url}}/data
call1.json:
"data": [
{
"id": "1",
"volume:" "2000"
},
{
"id": "2",
"volume:" "3000"
},
{
"id": "3",
"volume:" "4000"
},
{
"id": "4",
"volume:" "5000"
}
];
call2(req.body.id) - GET method - parsed JSON to JS object, route: {{url}}/data/:id
For example, if I pass req.body.id as = 1 got from the first response, it will open data for that id. So:
return call2(2) will return the data from this JSON: call2.json:
"data": [
{
"id": "1",
"add_data": "important string",
"add_data_2": "important string two"
},
];
The bottom line is, when doing the {{url}}/data route call - call() I need to serve all the data from {{url}}/data/:id routes, and bind them to the correct id. So, for example, the scenario I am trying to achieve is this:
Inside call(): get call1.json: data, do as many call2(req.body.id) calls as there are ids in the first object and then combine add_data and add_data_two values in the first object. So for example the final object would look like this.
console.log(response)
"data": [
{
"id": "1",
"volume:" "2000",
"add_data": "important string",
"add_data_2": "important string two"
},
{
"id": "2",
"volume:" "3000",
"add_data": "important string",
"add_data_2": "important string two"
},
{
"id": "3",
"volume:" "4000",
"add_data": "important string",
"add_data_2": "important string two"
},
{
"id": "4",
"volume:" "5000",
"add_data": "important string",
"add_data_2": "important string two"
}
];
This is what I have tried so far:
async get_data(req) {
try {
const objFirst = await call1(); //gets data
let objTwo = '';
for (let i = 0; i < objFirst.data.length; i++) {
objTwo = await call2({id: objFirst.data[i].id}) //gets data
}
return objFirst;
} catch(err) {
console.log("Error: ", err)
}
}
But it does not work. How can I get all data, and make as many as call2(id) as there are ids and combine that all in one object? Basically, I need to repeat this callback -> call2(id) as many ids we receive in call1().
Thanks, sorry if it looks like a mess.
You can use the map function and spread operator for this. Something like below.
Call2 function just simulates what an endpoint would return, but you get the idea.
var data = [
{
id: 1,
add_data: "1111"
},
{
id: 2,
add_data: "2222"
}
];
var data2 = [
{
id: 1,
volume: "bla"
},
{
id: 2,
volume: "bla"
}
];
function call2(id) {
return data2.filter(x => x.id == id)[0];
}
var result = data.map(x => {
var response = call2(x.id);
return {
...x,
...response
}
})
console.dir(result[0]);
The speed of your solution (loop through an array and doing http calls to get more data is really slow). If you have a lot of these functions that needs to combine data from different datasources, and depending on your project size, i would look into either RXJS or GraphQL (If you really need performance). RXJS have great functions to merge, combine, map etc data.
RXJS
GraphQL
Related
I am using json-server as my fake API data. I am implementing the search functionality to it. I created an endpoint like this -
getData : ( searchTerm : string ) => axios.get(`http://localhost:3000/books?=${searchTerm}`).then((response) => setData(response));
and I am utilizing into my input field to get the searched results.
Let's say My json object coming back from the Json-server is as follows -
[
{
"Id": 1,
"name" : "car"
},
{
"Id": 2,
"name" : "bike"
},
{
"Id": 3,
"name" : "ninja bike"
}]
now, the problem is , when I search for "car", it gives me the json result.
but, when I search for "brand new car", it should give me the "car's" object at least, as word "car" is a match. but it is giving me [], empty array.
So please suggest me how could i look for specific words into my json-server's data?
so that whenever , the end user even make a vague unstructured search, it should look for specific words like "car", in this case and return that car object.
You can make a simple filter to check if your string is in there
let json = [{
"Id": 1,
"name": "car"
},
{
"Id": 2,
"name": "bike"
},
{
"Id": 3,
"name": "ninja bike"
}
]
let searchString = "brand new car".split(" ") // ["brand", "new", "car"]
let filter = json.filter(json => searchString.includes(json.name))
if (filter.length) {
console.log(filter[0].name)
console.log(filter[0].Id)
}
else console.log("not found")
I have a nested array that looks like this:
[["Organisation","ID","Name"],["ACME","123456","Bart Simpson"],["ACME","654321","Ned Flanders"],["ACME","1234","Edna Kabappel"],["Yahoogle","666666","Bon Scott"],["Yahoogle","99999","Neil Young"],["Yahoogle","111111","Shania Twain"]]
The first value in each array is the name of an organisation that an ID and name can belong to.
I am trying to find the simplest way to group all instances where an ID and name belong to the same company, under one 'key'.
So the above would result in something like this:
{
"ACME": [
{
"ID": 123456,
"Name": "Bart Simpson"
},
{
"ID": 654321,
"Name": "Ned Flanders"
},
{
"ID": 1234,
"Name": "Edna Kabappel"
}
],
"Yahoogle": [
{
"ID": 666666,
"Name": "Bon Scott"
},
{
"ID": 99999,
"Name": "Neil Young"
},
{
"ID": 111111,
"Name": "Shania Twain"
}
]
}
I have been playing around with for loops but I'm ending up with many many lines of code, trying to detect when the company name is different from the previous, and getting into a real mess with things.
I have searched a lot here trying to find something similar but have not had any luck.
I have only just started coding again for person interest after about 18 years and I'm very novice again.
Thank you in advance for any assistance.
lot of solutions to arrive at same result, one using lambda and reduce: this is a generic solution, just adapt the output push to build your final json.
const datas = [
["Organisation", "ID", "Name"],
["ACME", "123456", "Bart Simpson"],
["ACME", "654321", "Ned Flanders"],
["ACME", "1234", "Edna Kabappel"],
["Yahoogle", "666666", "Bon Scott"],
["Yahoogle", "99999", "Neil Young"],
["Yahoogle", "111111", "Shania Twain"]
];
const titles = datas.shift()
const groupBy = (x,f)=>x.reduce((a,b)=>((a[f(b)]||=[])
.push({[titles[1]]:b[1], [titles[2]]:b[2]}),a),{});
const result = groupBy(datas, v => v[0])
console.log(result)
Using Array.reduce. Please check if this works for you. In the below approach ID and Name is hard coded. You can try writing a generic dynamic approach which handle any number of params like ID, Name, Age etc.
const myArray = [
["Organisation", "ID", "Name"],
["ACME", "123456", "Bart Simpson"],
["ACME", "654321", "Ned Flanders"],
["ACME", "1234", "Edna Kabappel"],
["Yahoogle", "666666", "Bon Scott"],
["Yahoogle", "99999", "Neil Young"],
["Yahoogle", "111111", "Shania Twain"]
];
const obj = myArray.reduce((acc, value, index) => {
if (index === 0) return acc;
const key = value[0];
if (!acc[key]) {
acc[key] = [];
}
acc[key].push({
ID: value[1],
Name: value[2]
});
return acc;
}, {});
console.log(obj);
In order to achieve what you want, you can follow below steps:
Create an object to store your result.
While you are running the loop you have to check whether name of the organization exists as a key in the object and add it if it does not, initializing it to an empty array. Then push the result you want to store into that array.
Below is a sample implementation, assuming your data is stored in data:
var result = {};
for(var i=1; i < data.length; i++){
if(!result.hasOwnProperty(data[i][0])){
result[data[i][0]] = [];
}
result[data[i][0]].push({ "ID": data[i][1], "Name": data[i][2]});
}
I've been trying to wrap my head around filtering arrays of objects for a while now, but I can't seem to really get a hang on it. Although I usually have working code in the end, it just doesn't look like elegant code to me. So, I'd appreciate a code review and some hints very much!
Example:
I'm currently working on this example for an online shop where I need to retrieve product details out of an array of objects based on an id.
This is my helper function:
function getItemDetails(id) {
var getCategory = shelf.filter(obj => obj.articleList.some(cat => cat.id === id));
var getArticleList = getCategory[0].articleList;
var getItem = getArticleList.filter(item => item.id == id);
return getItem
}
Steps: In a first step, I tried to filter the shelf array, but it would return the entire array articleList of the corresponding item.
So, I filtered the result again with the same criteria and it works, but it just looks awfully redundant to me.
This is an example of the data:
const shelf = [{
"categoryPrice": "2",
"categoryTitle": "Flyer",
"articleList": [{
"id": "1",
"articleTitle": "Green",
}, {
"id": "2",
"articleTitle": "Blue",
}],
}, {
"categoryPrice": "3",
"categoryTitle": "Post card",
"articleList": [{
"id": "3",
"articleTitle": "Purple"
}, {
"id": "4",
"articleTitle": "Yellow",
}]
}]
I checked various questions here, including:
Vue/Javascript filter in deep object inside object array
Deep filter objects in javascript array
But none of them provide an easier, more concise solution, in my opinion. What am I missing?
Thanks for your help!
This might be a better fit for codereview but if the question is just 'How to make this more concise' I would suggest something like following:
const shelf = [{
"categoryPrice": "2",
"categoryTitle": "Flyer",
"articleList": [{
"id": "1",
"articleTitle": "Green",
}, {
"id": "2",
"articleTitle": "Blue",
}],
}, {
"categoryPrice": "3",
"categoryTitle": "Post card",
"articleList": [{
"id": "3",
"articleTitle": "Purple"
}, {
"id": "4",
"articleTitle": "Yellow",
}]
}]
const findItem = function(shelves, id) {
return shelves.flatMap((shelf) => shelf.articleList).find((article) => article.id == id) || null;
}
console.log(findItem(shelf, 1));
console.log(findItem(shelf, 3));
The above example concatenate all the list of articles and then searches that array for the article with the supplied ID.
Performance wise? Not the best, but you asked for something concise and this is about as concise as one can hope for with the given data structure.
This code is O(1), which means that lookup per article.id is constant. It will however use more memory. To conserve memory, I used WeakMap, as long as you use the same shelf variable, it will not recompute it. But once you replace it, it will also perish from the cache.
const shelf = [{
"categoryPrice": "2",
"categoryTitle": "Flyer",
"articleList": [{
"id": "1",
"articleTitle": "Green",
}, {
"id": "2",
"articleTitle": "Blue",
}, {
"id": "3", // Added
"articleTitle": "Violet",
}],
}, {
"categoryPrice": "3",
"categoryTitle": "Post card",
"articleList": [{
"id": "3",
"articleTitle": "Purple"
}, {
"id": "4",
"articleTitle": "Yellow",
}],
}];
const findItems = function(shelves, id) {
if (!findItems._map) {
// Create computation cache holder
// Weak map will make sure, that if the object is disposed, it can be garbage collected, with it will be gone its cache too! (That is awsome!)
findItems._map = new WeakMap();
}
if (!findItems._map.has(shelves)) {
// For every shelves object, we will create a new Map containing all mapped values.
const map = new Map();
findItems._map.set(shelves, map);
shelves.forEach(shelf => {
shelf.articleList.forEach(article => {
if (!map.has(article.id)) {
// If list is not yet created create it with the article
return map.set(article.id, [ article ]);
}
// If it exists, add to it
map.get(article.id).push(article);
});
});
}
return findItems._map.get(shelves).get(id);
}
console.log(findItems(shelf, "1"));
console.log(findItems(shelf, "3"));
You can get away with looping twice. Once for the outer array and once for the articleList array
const findItem = (id) =>
shelf.reduce((acc, current) => {
const found = current.articleList.find((x) => x.id === id);
if (found) return [...acc, found];
return acc;
}, []);
This API require me to send some data comma separated, when handling the user input I use checkboxes like so
<SelectMultiple
items={dairy}
selectedItems={this.state.selectedIngredients}
onSelectionsChange={this.onSelectionsChange} />
I can definitely see the inputs if I render selectedIngredients on a FlatList (using item.value) but when I try to print the actual url I am working with I got this [object,Object]
I used this.state.selectedIngredients in the view, here is the result:
url.com/api/?=[object Object],[object Object],[object Object]
Inside my search function I use that code to handle user inputs:
const { selectedIngredients } = this.state;
let url = "url.com/api/?i=" + selectedIngredients
In the documentation of the library they say the selected items is type array of string or { label, value } I used the method provided there to append the selected items:
onSelectionsChange = (selectedIngredients) => {
// selectedFruits is array of { label, value }
this.setState({ selectedIngredients})
}
I tried adding .value on both of them it didn't solve my problem. Could anyone one help please?
Console log this.state.selectedIngredients:
Array [
Object {
"label": "Goat cheese",
"value": "Goat cheese",
},
Object {
"label": "Cream cheese",
"value": "Cream cheese",
},
Object {
"label": "Butter",
"value": "Butter",
},
]
selectedIngredients = [
{
"label": "Goat cheese",
"value": "Goat cheese",
},
{
"label": "Cream cheese",
"value": "Cream cheese",
},
{
"label": "Butter",
"value": "Butter",
},
]
let selectedValues = selectedIngredients.map((ingredient)=> ingredient.value)
let selectedValuesInString = selectedValues.join(',');
console.log(selectedValuesInString)
It's a bit clumsy, but it'll give you a comma-separated string with no trailing comma.
const { selectedIngredients } = this.state;
let sep = "";
let selectedIngAsString = "";
selectedIngredients.forEach(s => {
selectedIngAsString += sep + s.value;
sep = ",";
});
let url = "url.com/api/?i=" + selectedIngAsString
See https://codesandbox.io/s/m5ynqw0jqp
Also, see Mohammed Ashfaq's for a much less clumsy answer.
I have an JSON array like this
var filter_value_data = [{"Status":[{"name":"Open","id":"1"},{"name":"Pending","id":"2"},{"name":"Resolved","id":"3"},{"name":"Closed","id":"4"},{"name":"Evaluation","id":"5"}]},{"Payment Status":[{"name":"Paid","id":"10"},{"name":"UnPaid","id":"11"},{"name":"Part Paid","id":"12"}]},{"Priority":[{"name":"Low","id":"6"},{"name":"Medium","id":"7"},{"name":"High","id":"8"},{"name":"Urgent","id":"9"}]}]
I have tried filter_value_data["Status"] which is obviously wrong. How do I get the JSON elements for Status using the names like Status,Payment Status?
filter_value_data is an array (having []), so use filter_value_data[0].Status to get the first element-object with property "Status".
It is always good to format your code in order to see the hierarchy of the structures:
var filter_value_data = [
{
"Status": [
{
"name": "Open",
"id": "1"
}, {
"name": "Pending",
"id": "2"
}, ...
]
}, {
"Payment Status": [
{
"name": "Paid",
"id": "10"
}, ...
]
}, {
"Priority": [
{
"name": "Low",
"id": "6"
}, ...
]
}
];
With your current JSON you can't get the elements with the name alone.
You can get Status with filter_value_data[0]['Status'] and Payment status with filter_value_data[1]['Payment Status'].
This is because the keys are in seperate objects in the array.
In order to get them with filter_value_data['Status'] you need to change your JSON to
var filter_value_data = {
"Status":[
{"name":"Open","id":"1"},
{"name":"Pending","id":"2"},
{"name":"Resolved","id":"3"},
{"name":"Closed","id":"4"},
{"name":"Evaluation","id":"5"}
],
"Payment Status":[
{"name":"Paid","id":"10"},
{"name":"UnPaid","id":"11"},
{"name":"Part Paid","id":"12"}
],
"Priority":[
{"name":"Low","id":"6"},
{"name":"Medium","id":"7"},
{"name":"High","id":"8"},
{"name":"Urgent","id":"9"}
]
};
I wrote this on my phone so it's not as well-formatted as usual. I'll change it ASAP.
With your current JSON, created a result which might be helpful for you.
JS:
$.each(filter_value_data,function(ind,val){
var sta = val.Status; // Status Object get displayed
for(var i=0;i<sta.length;i++){
var idVal= sta[i].id;
var nameVal = sta[i].name;
Statusarray.push(idVal,nameVal);
console.log(Statusarray);
}
})
FiddleDemo
You can use below code, it will return status object
filter_value_data[0]['Status']
filter_value_data[0]['Payment Status']
to get Single value you use :
filter_value_data[0]['Status'][0]['name']