recursive function to search in json object in javascript - javascript

I am trying to write a recursive function in javascript but not work properly. i have a json array of objects data where i want to find something based on key then find again based on gotopage key in search object.
like : find orange -> gotopage -> orange_store ->find -> orange_store -> gotopage -> yellow_store -> find so the same process goes in recursively.can you please help where i'm going wrong in my approach.
[
{
"id": 1,
"find": "orange",
"gotopage": "orange_store"
},
{
"id": 2,
"find": "orange_store",
"gotopage": "yellow_store"
},
{
"id": 3,
"find": "black_store",
"gotopage": "black_store"
},
{
"id": 4,
"find": "yellow_store",
"gotopage": "white_store"
},
{
"id": 5,
"find": "black_store",
"gotopage": "red_store"
}
]
function searchRec(search, myArray) {
for (var i = 0; i < myArray.length; i++) {
var res = [];
if (myArray[i].find == search) {
if (myArray[i] !== null) {
console.log(myArray[i]);
res = searchRec(myArray[i].gotopage, myArray);
if (res !== null) {
return res;
}
return myArray[i];
}
}
}
}
function findNode(arr) {
for (i = 0; i < arr.length; i++) {
searchRec(arr[i].find, arr);
break;
}
}
console.log(findNode(json));
output for first iteration but not work for every iteration:
Object {id: 1, find: "orange", gotopage: "orange_store"}
Object {id: 2, find: "orange_store", gotopage: "yellow_store"}

Another example using recursion. I do a simple forEach() to find what you're looking for and store it in variables, log it, and re-call the function with our newly created values. If it doesn't find anything, it returns null and ends.
const data = [
{
"id": 1,
"find": "orange",
"gotopage": "orange_store"
},
{
"id": 2,
"find": "orange_store",
"gotopage": "yellow_store"
},
{
"id": 3,
"find": "black_store",
"gotopage": "black_store"
},
{
"id": 4,
"find": "yellow_store",
"gotopage": "white_store"
},
{
"id": 5,
"find": "black_store",
"gotopage": "red_store"
}
];
function recursiveStore(search, myArray) {
let obj = {}
let newSearch;
data.forEach(store => {
if (search === store.find) {
obj = store
newSearch = store.gotopage
}
})
if (Object.keys(obj).length === 0) {
return null
}
console.log(obj)
recursiveStore(newSearch, myArray)
}
recursiveStore("orange", data)

Related

filter object by two nested values

I'm facing a problem with filter method. On my page there's an input to search matches by team names. Filter value is being stored to React state. Matches object looks like this:
[
{
"id": 4,
"teamBlue": {
"id": 36,
"name": "nameForTeamBlue",
"playerList": [
{
[...]
}
]
},
"teamRed": {
"id": 37,
"name": "nameForTeamRed",
"playerList": [
{
[...]
}
]
},
"localDate": "2020-01-01",
"localTime": "00:00:00",
"referee": null,
"commentator1": null,
"commentator2": null,
"streamer": null,
"stage": {
"id": 2,
"name": "GROUPSTAGE"
},
"onLive": true,
"finished": false
},
]
I tried tons of methods to filter matches by team name, for example:
let criteria = {
teamBlue: {
name: this.state.filter
},
teamRed: {
name: this.state.filter
}
};
let filteredMatches = this.state.matches.filter(function(item) {
for (let key in criteria) {
if (item[key] === undefined || item[key] !== criteria[key])
return false;
}
return true;
});
console.log(filteredMatches);
but none of them worked.
Is there any way to filter these matches so when I type "blue" into my input, it will show all matches where team name contains "blue"?
Thanks in advance!
Try updating the condition to:
if (!item[key] || item[key].name !== criteria[key].name)
let filteredMatches = this.state.matches.filter(function(item) {
let flag = true;
for (let key in criteria) {
// update this to
if (!item[key] || item[key].name !== criteria[key].name)
flag = false;
}
return flag;
});
The name property is missing :
if (key in item && item[key].name !== criteria[key].name)
You're comparing objects with === which will return false. You either need to use a deep comparison method from a library, or implement it yourself like below:
const matches = [ {"id": 4,
"teamBlue": {
"id": 36,
"name": "nameForTeamBlue",
"playerList": []
},
"teamRed": {
"id": 37,
"name": "nameForTeamRed",
"playerList": []
},
}, {"id": 4,
"teamBlue": {
"id": 36,
"name": "nameForTeamBlue",
"playerList": []
},
"teamRed": {
"id": 37,
"name": "nameForTeamRead",
"playerList": []
},
}]
const criteria = {
teamBlue: {
name: 'nameForTeamBlue',
},
teamRed: {
name: 'nameForTeamRed',
}
}
const filteredMatches = matches.filter((item) => {
const allCriteriaMatched = Object.entries(criteria)
.every(([key, value]) => {
const matched = Object.entries(value).every(([criteriaKey, criteriaValue]) => {
const itemValue = item[key][criteriaKey]
const matched = itemValue == criteriaValue
if (!matched) console.log('Item %s does not matched criteria %s. Item\'s value is %s, but criteria value is %s', item[key]['id'], criteriaKey, itemValue, criteriaValue, criteriaValue)
return matched
})
if (!matched) return false
return true
}, {})
return allCriteriaMatched
})
console.log(filteredMatches);
Basically, you just need to go 1 level deeper :D if your criteria can have multiple nested objects, then there's no point doing it manually. You can try to map criteria to run against matches so that you don't use === on objects, but only primitives.

how to get the corresponding type from fruit names array?

Get the corresponding type in the object, and then traverse the array of push objects, but I can't think of a better way to solve the desired result below.
I want a good return as follows:
[{
"id": 1,
"type": "one",
"name": ["apple","apricot"]
},
{
"id": 3,
"type": "two",
"name": ["avocado"]
}]
var result = [{
"id": 1,
"type": "one",
"name": "apple"
}, {
"id": 2,
"type": "one",
"name": "apricot"
},
{
"id": 3,
"type": "two",
"name": "avocado"
}
]
Array.prototype.unique = function() {
var hash = {},
len = this.length,
result = [];
for (var i = 0; i < len; i++) {
if (!hash[this[i].type]) {
result.push(this[i].type);
hash[this[i].type] = true;
}
}
return result;
}
console.log(result)
console.log(result.unique())
var cArr = result.unique()
var arr = []
cArr.forEach(function(prop) {
result.map(function(item) {
if (prop == item.type) {
console.log(item)
arr.push({
...item,
[`user_${item.id}`]: item.user,
})
}
})
})
console.log(arr)
You can do this with reduce quite easily:
var input = [
{ id: 1, type: "one", name: "apple"},
{ id: 2, type: "one", name: "apricot" },
{ id: 3, type: "two", name: "avocado" }
];
// Make sure `unique` doesn't already exist on the Array prototype
if (!('unique' in Array.prototype)) {
Array.prototype.unique = function () {
// iterate over the array
const temp = this.reduce((acc, current) => {
// Desstructure the id, type, and name from the current object
const { id, type, name } = current;
// If an key with the value of `type` doesn't exist
// on the accumulator, add a new object with name set
// to an empty array
acc[type] = acc[type] || { id, type, name: [] };
// Push the name in the current object to the name array
acc[type].name.push(name);
// Return the accumulator for the next iteration
return acc;
// Note: the initial accumulator value is an object
}, {});
// Then simply return the values from the accumulated object
return Object.values(temp);
}
}
console.log(input.unique())

How to get one json object format from another JSON format

Maybe this question has already been asked and answered somewhere but after searching for more than 3 hrs I'm asking this question.
Below is my JSON data
var my_data = [
{
"TempRture_qc": 4,
"VoltAGE": 44.09722,
"TempRture": 22.32,
"VoltAGE_qc": 55,
"_time": "2018-08-07T03:39:29.001Z"
},
{
"TempRture_qc": 2,
"VoltAGE": 42.09722,
"TempRture": 22.12,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:30.006Z"
},
{
"TempRture_qc": 1,
"VoltAGE": 43.09722,
"TempRture": 22.82,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:31.009Z"
}
];
desired output i need
[
{
"name": "TempRture_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":4},
{"name":"2018-08-07T03:39:30.006Z","y":2},
{"name":"2018-08-07T03:39:33.017Z","y":1}
]
},
{
"name": "VoltAGE",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":44.09722},
{"name":"2018-08-07T03:39:30.006Z","y":42.09722},
{"name":"2018-08-07T03:39:33.017Z","y":43.09722}
]
},
{
"name": "TempRture",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":22.32},
{"name":"2018-08-07T03:39:30.006Z","y":22.12},
{"name":"2018-08-07T03:39:33.017Z","y":22.82}
]
},
{
"name": "VoltAGE_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":55},
{"name":"2018-08-07T03:39:30.006Z","y":0},
{"name":"2018-08-07T03:39:33.017Z","y":0}
]
}
]
for getting this above output i have tried below code.
var accounting = [];
var fieldName = {};
for (var x in obj){
var mykey = Object.keys(obj[x]);
for (var mk in mykey){
if(mykey[mk]=='VoltAGE'){
fieldName.name = mykey[mk];
// accounting.push({
// "name":mykey[mk]
// })
}
if(mykey[mk]=='TempRture'){
fieldName.name = mykey[mk];
}
// console.log(mykey[mk]); //to get the key name
}
accounting.push({
"name" : obj[x]._time,
"y" : obj[x][employees.name],
})
fieldName.data = accounting;
}
console.log(fieldName );
by doing this what I'm getting is below JSON
{ name: 'TempRture',
data:
[ { name: '2018-08-07T03:39:29.001Z', y: 22.32 },
{ name: '2018-08-07T03:39:32.014Z', y: 22.12 },
{ name: '2018-08-07T03:39:33.017Z', y: 22.82 } ] }
I'm not able to understand how I will get the data in one JSON object.
For a solution with low time complexity, try .reduceing into an object indexed by keys of the inner object, creating a { name, data: [] } at that key in the accumulator if it doesn't exist there yet. Then, push to the data array, and get the values of the whole object:
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
console.log(Object.values(
my_data.reduce((a, { _time, ...obj }) => {
Object.entries(obj).forEach(([name, val]) => {
if (!a[name]) a[name] = { name, data: [] };
a[name].data.push({ name: _time, y: val });
});
return a;
}, {})
));
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
var keys = Object.keys(my_data[0])
var result= [];
for(i = 0; i<keys.length-1; i++) {
var obj = {name: keys[i],data: []}
obj.data = my_data.map(val=>({name: val["_time"], y: val[keys[i]]}));
result.push(obj);
}
console.log(result)
An understandable answer with map, findIndex and forEach functions will be
var my_data = [{ "TempRture_qc": 4, "VoltAGE": 44.09722, "TempRture": 22.32, "VoltAGE_qc": 55, "_time": "2018-08-07T03:39:29.001Z" }, { "TempRture_qc": 2, "VoltAGE": 42.09722, "TempRture": 22.12, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:30.006Z" }, { "TempRture_qc": 1, "VoltAGE": 43.09722, "TempRture": 22.82, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:31.009Z" } ],
result = [];
my_data.map(itm => {
let keys = Object.keys(itm);
keys.forEach(iitt => {
if (iitt != '_time') {
let index = result.findIndex(ii => {
return ii.name == iitt;
})
if (index == -1) {
result.push({
name: iitt,
data: []
});
result[result.length - 1].data.push({
name: itm["_time"],
y: itm[iitt]
})
} else {
result[index].data.push({
name: itm["_time"],
y: itm[iitt]
});
}
}
})
})
console.log(result)

Is there a way to for an object filter to pass the object without a named reference?

I have the following javascript code which produces the desired results, i.e. returns both the 3rd and 4th objects in objectsArray since they both contain the max distance. However, I'm wondering if there is a way to not have to retype the name of the array when calling objectsArray.filter? I'm not trying to be lazy, just avoiding redundancy and the possibility of introducing a typo.
function meetsMax(obj) {
return obj.distance === Math.max.apply(Math, this.map(function(o) { return o.distance; }));
}
const objectsArray = [{ "distance": 1, "name": "first" }, { "distance": 2, "name": "second" }, { "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" }];
const objMax = objectsArray.filter(meetsMax, objectsArray);
console.log("objMax:", objMax);
I certainly wouldn't mind any other pointers on making the code more efficient and performant.
Function calls in JavaScript have some overhead, so native code is more efficient and performant:
var a = [ { "distance": 1, "name": "first" }, { "distance": 2, "name": "second" },
{ "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" } ]
for (var o = a[0], objMax = [o], m = o.distance, d, i = 1; i < a.length; i++)
if ((d = (o = a[i]).distance) > m) { objMax = [o]; m = d }
else if (d === m) objMax[objMax.length] = o
console.log(JSON.stringify(objMax))
There are also shorter and less efficient alternatives:
var a = [ { "distance": 1, "name": "first" }, { "distance": 2, "name": "second" },
{ "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" } ]
var d, b = []; a.forEach(o => (b[d = o.distance] = b[d] || []).push(o))
console.log(JSON.stringify(b[b.length - 1]))
Why don't you use for loop? It will be faster than your code.
"use strict";
let start = performance.now();
for (let z = 0; z < 1000; z++) {
function meetsMax(obj) {
return obj.distance === Math.max.apply(Math, this.map(function(o) { return o.distance; }));
}
const objectsArray = [{ "distance": 1, "name": "first" }, { "distance": 2, "name": "second" }, { "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" }];
const objMax = objectsArray.filter(meetsMax, objectsArray);
}
let fin = performance.now() - start;
console.log(fin); // 3.25ms
"use strict";
let start = performance.now();
for (let z = 0; z < 1000; z++) {
let a = [{ "distance": 1, "name": "first" }, { "distance": 2, "name": "second" }, { "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" }];
let maxDistance = 0;
let result = [];
for (let i = 0, max = a.length; i < max; i++) {
if (a[i].distance > maxDistance) {
maxDistance = a[i].distance;
}
}
for (let i = 0, max = a.length; i < max; i++) {
if (a[i].distance === maxDistance) {
result.push(a[i]);
}
}
}
let fin = performance.now() - start;
console.log(fin); // 1.28ms
.filter passes three arguments to the array: the current value, the index of the current value and the array itself. So you can change your filter function to:
function meetsMax(obj, index, objectsArray) {
return obj.distance === Math.max.apply(Math, objectsArray.map(function(o) { return o.distance; }));
}
and call .filter with
objectsArray.filter(meetsMax);
Always read the documentation of the functions you are using.
I certainly wouldn't mind any other pointers on making the code more efficient and performant.
If you, compute the maximum distance only once instead of in every iteration of the array. E.g. you could do:
function filterMax(arr, extractor) {
const max = arr.reduce(function(max, item) {
return max < extractor(item) ? extractor(item) : max;
}, extractor(arr[0]));
return arr.filter(function(item) {
return extractor(item) === max;
});
}
and call it as
filterMax(objectsArray, function(obj) { return obj.distance; });
function filterMax(arr, extractor) {
const max = arr.reduce(function(max, item) {
return max < extractor(item) ? extractor(item) : max;
}, extractor(arr[0]));
return arr.filter(function(item) {
return extractor(item) === max;
});
}
const objectsArray = [{ "distance": 1, "name": "first" }, { "distance": 2, "name": "second" }, { "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" }];
console.log(filterMax(objectsArray, function(obj) {
return obj.distance;
}));
According to MDN's Array.prototype.filter(), the array name is an optional override to the internal value of this.
So to answer the original question:
I'm wondering if there is a way to not have to retype the name of the array when calling objectsArray.filter?
Yes, you can safely leave it out.
var filter = function(x) { if (x > 5) return true; };
var arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
alert(arr.filter(filter).join(","));
or even simpler (albeit harder to read):
alert([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].filter(function(x) { if (x > 5) return true; }));
You specifically asked about an object, but you're not filtering objects, you're filtering an array of objects, so same applies.
console.log([ {foo: 1}, {foo: 2}, {foo: 3}, {foo: 4}, {foo: 5}, {foo: 6}, {foo: 7}, {foo: 8}, {foo: 9}, {foo: 10}].filter(function(x) { if (x.foo > 5) return true; }));

How to add new data from array of string to array of object sequentially?

Suppose I have array of object:
originalData = [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
},
{
"id": 4
},
{
"id": 5
},
{
"id": 6
}
]
And I have array of string:
newData = ['1','2','3']
How do I push newData to originalData sequentially?
Expected result should be like so:
originalData = [
{
"id": 1,
"color":'1'
},
{
"id": 2,
"color":'2'
},
{
"id": 3,
"color":'3'
},
{
"id": 4,
"color":'1'
},
{
"id": 5,
"color":'2'
},
{
"id": 6,
"color":'3'
}
]
Here's my workaround:
originalData.forEach(function (object,i) {
object.color = newData[i]
});
Use a variable j and reset it over time.
var j = 0;
originalData.forEach(function (object,i) {
object.color = newData[j];
j += 1;
if(j > newData.length)
j = 0;
});
Reset the counter dynamically depending on the newData array length.
originalData = [{"id": 1},{"id": 2},{"id": 3},{"id": 4},{"id": 5},{"id": 6}];
newData = ['1','2','3'];
for(var i = 0, j=0; i < originalData.length; i++, j++) {
originalData[i].color = newData[j];
if(j == newData.length -1){
j = -1;
}
}
console.log(originalData);

Categories

Resources