Iterate array of objects by dimension, based on given limit - javascript

I have a multi-dimensional array of objects which contain suggested users.
Again each suggested user has some suggested users and so on.
In the application I'm building I select a user (let's call him 'Steve') and would like to receive a certain amount of users connected to that user (by fetching the suggestions). The data is all there, I just need to find a meaningful way to iterate this.
To simplify I have built an array with some sample data where each user has two suggested users:
// Suggested users of my initially selected user (Steve)
let suggested_users_based_on_steve = [
{
name: 'A',
suggested: [
{
name: 'A1',
suggested: [
{ name: 'A1A1' },
{ name: 'A1A2' }
]
},
{
name: 'A2',
suggested: [
{ name: 'A2A1' },
{ name: 'A2A2' }
]
}
]
},
{
name: 'B',
suggested: [
{
name: 'B1',
suggested: [
{ name: 'B1B1' },
{ name: 'B1B2' }
]
},
{
name: 'B2',
suggested: [
{ name: 'B2B1' },
{ name: 'B2B2' }
]
}
]
}
];
Let's say for example I need to get 7 suggested users based on my user Steve.
That means I would iterate the first dimension and get the first two users (A and B).
Since now I have fetched only 2 of 7 users, I need to go one dimension deeper and get the suggested users of A and B which are A1, A2, B1 and B2. This would add up to 6 out of 7 required users, so I need one more.
Therefore I also fetch one suggested user of A1, which is A1A1. Now I need to break / stop the iteration because I'm done.
Please note that the amount of suggested users is not fixed. Every user can have an unlimited amount of suggested users. Also, it's important iterate from layer to layer to get the 'best' and most connected results / users. The initial user is more connected to A and B than to A1A1 and A1A2.
I'm struggling with finding the right combination of nested for...of loops. Is there any recommended way on doing this?
Thank you in advance!

let suggested_users_based_on_steve = [
{
name: 'A',
suggested: [
{
name: 'A1',
suggested: [
{ name: 'A1A1' },
{ name: 'A1A2' }
]
},
{
name: 'A2',
suggested: [
{ name: 'A2A1' },
{ name: 'A2A2' }
]
}
]
},
{
name: 'B',
suggested: [
{
name: 'B1',
suggested: [
{ name: 'B1B1' },
{ name: 'B1B2' }
]
},
{
name: 'B2',
suggested: [
{ name: 'B2B1' },
{ name: 'B2B2' }
]
}
]
}
];
function test(arr, result = []) {
for (let user of arr.values()) {
result.push(user);
if (result.length === 7) return result;
}
var next = arr.reduce(function (all, user) {
return all.concat(user.suggested || []);
}, []);
if (next.length === 0) return result;
return test(next, result);
}
test(suggested_users_based_on_steve);

Related

Return all values of nested arrays using string identifier

Given an object searchable, is there a simple way of returning all the id values using lodash or underscore.js (or equivalent) where I can define the path to id?
const searchable = {
things: [
{
id: 'thing-id-one',
properties: [
{ id: 'd1-i1' },
{ id: 'd1-i2' },
]
},
{
id: 'thing-id-two',
properties: [
{ id: 'd2-i1' },
{ id: 'd2-i2' },
]
}
]
}
I am looking to see if this is possible in a manner similar to how we can use lodash.get e.g. if we wanted to return the things array from searchable we could do
const things = _.get(searchable, 'things');
I can't seem to find anything similar in the documentation. I am looking for something
that could contain an implementation similar to:
_.<some_function>(searchable, 'things[].properties[].id')
Note: I am well aware of functions like Array.map etc and there are numerous ways of extracting the id property - it is this specific use case that I am trying to figure out, what library could support passing a path as a string like above or does lodash/underscore support such a method.
Found a solution using the package jsonpath
const jp = require('jsonpath');
const result = jp.query(searchable, '$.things[*].properties[*].id')
console.log(result);
// outputs: [ 'd1-i1', 'd1-i2', 'd2-i1', 'd2-i2' ]
you can do it easily in plain js
like this
const searchable = {
things: [
{
id: 'thing-id-one',
properties: [
{ id: 'd1-i1' },
{ id: 'd1-i2' },
]
},
{
id: 'thing-id-two',
properties: [
{ id: 'd2-i1' },
{ id: 'd2-i2' },
]
}
]
}
const search = (data, k) => {
if(typeof data !== 'object'){
return []
}
return Object.entries(data).flatMap(([key, value]) => key === k ? [value]: search(value, k))
}
console.log(search(searchable, 'id'))
_.map and _.flatten together with iteratee shorthands let you expand nested properties. Every time you need to expand into an array, just chain another map and flatten:
const searchable = {
things: [
{
id: 'thing-id-one',
properties: [
{ id: 'd1-i1' },
{ id: 'd1-i2' },
]
},
{
id: 'thing-id-two',
properties: [
{ id: 'd2-i1' },
{ id: 'd2-i2' },
]
}
]
}
// Let's say the path is "things[].properties[].id"
const result = _.chain(searchable)
.get('things').map('properties').flatten()
.map('id').value();
console.log(result);
<script src="https://cdn.jsdelivr.net/npm/underscore#1.13.4/underscore-umd-min.js"></script>

Filter array of objects by value

I want to filter an array of objects, by a specific value within the objects.
In the example i've provided I want to filter the array 'pets' by a value in the array 'characteristics'. For example, where I have called the function with the param 'loyal', i'd only expect the object for the dog value to be returned, as only the dog has that characteristic.
At the moment when I call the function both objects are returned even though only the object for dog has that value in its characteristics array.
const pets = [
{
name: 'dog',
characteristics: [
{
value: 'loyal'
},
{
value: 'big'
}
]
},
{
name: 'cat',
characteristics: [
{
value: 'fluffy'
},
{
value: 'small'
}
]
},
]
function filterPets(pets, characteristic) {
return pets.filter(function(pet) {
return pet.characteristics.filter(o => o.value.includes(characteristic));
})
}
console.log(filterPets(pets, 'loyal'));
That's because for the characteristics check you're using filter, which always returns an array (even if a blank one), and even a blank array is a truthy value, so the outer filter keeps every pet you check. For that inner check, you want some, not filter, so you get a flag for whether any entries matched:
function filterPets(pets, characteristic) {
return pets.filter(function(pet) {
return pet.characteristics.some(o => o.value.includes(characteristic));
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^
});
}
const pets = [
{
name: 'dog',
characteristics: [
{
value: 'loyal'
},
{
value: 'big'
}
]
},
{
name: 'cat',
characteristics: [
{
value: 'fluffy'
},
{
value: 'small'
}
]
},
];
function filterPets(pets, characteristic) {
return pets.filter(function(pet) {
return pet.characteristics.some(o => o.value.includes(characteristic));
});
}
console.log(filterPets(pets, 'loyal'));
Just for what it's worth, I assume characteristics are unique (you can't have "loyal" twice), so you might prefer to keep those in a Set so you can check for them more easily than .some(o => o.includes(characteristic)). For instance:
const pets = [
{
name: "dog",
characteristics: new Set(["loyal", "big"]),
},
{
name: "cat",
characteristics: new Set(["fluffy", "small"]),
},
];
function filterPets(pets, characteristic) {
return pets.filter(function(pet) {
return pet.characteristics.has(characteristic);
});
}
Live Example:
const pets = [
{
name: "dog",
characteristics: new Set(["loyal", "big"]),
},
{
name: "cat",
characteristics: new Set(["fluffy", "small"]),
},
];
function filterPets(pets, characteristic) {
return pets.filter(function(pet) {
return pet.characteristics.has(characteristic);
});
}
console.log(filterPets(pets, "loyal"));
console.log("Don't worry about the {} for characteristics, the Stack Snippets console doesn't know how to display Set objects. Look in the real console if you want to double-check the set.");
function filterPets(list, charValue) {
const filteredPets = []
list.map(function(pet,petIndex,array) {
pet.characteristics.map(function(charac){
if(charac.value === charValue){
return filteredPets.push(array[petIndex])
}
})
})
return filteredPets
}
filterPets(pets,'loyal');

json-rules-engine processing array of objects

Any help would be appreciated !! I couldn't find an answer.
Given input facts to the engine such as -
const facts = {
cart: {
prop1: true,
prop2: "My Cart",
prop3: {
prop4: "North America"
},
products: [
{
category: 1,
classification: null
},
{
category: 2,
classification: null
},
{
category: 1,
classification: null
}
]
}
};
I want to be able to process each object in the products array and define a separate rule for each category and set the classification field for that category.
Something like -
let condition1 = {
all: [
{
fact: 'category',
operator: 'equal',
value: 1
}
]
};
let condition2 = {
all: [
{
fact: 'category',
operator: 'equal',
value: 2
}
]
};
let event1 = {
type: 'category 1 event'
// Update classification
};
let event2 = {
type: 'category 2 event'
// Update classification
};
How do I do this? Do i set the classification field in the event handler for that event? How do I access the object the category belongs to?
I tried using the below, but it selects all the category values from each object. I want to be able to process each object separately.
let condition = {
all: [
{
fact: 'cart',
operator: 'contains',
value: 1,
path: '$.products[*].category'
}
]
}
I cant add them as runTime facts either since you can have only 1 runtime fact and its value. (No Duplicates)

Identify circular dependency in a Json object and remove all element after 2 depth

I have a json object something like this:
var temp1 = {
name: "AMC",
children: [
{
name: "cde",
children: [
{
name: "AMC",
children: [
{
name: "cde",
children: [
{
name: "AMC",
children: [
//.............. continues as curcular depndency
]
}
]
}
]
}
]
},
{
name: "mnp",
children: [
{
name: "xyz",
children: []
}
]
}
]
}
Due to this cicular dependency, JSON.stringify is failing.
I have done enough google and searching to get the solution for this but could not find much help.
So here basically I want to detect a circular dependency in the json object and add a new key to the object, saying cricular: true and remove all the subsequent node.
So here is the result output what I am looking :
var temp1 = {
name: "AMC",
children: [
{
name: "cde",
circular: true,
children: [ // No children here as it is curcular dependency
]
},
{
name: "mnp",
children: [
{
name: "xyz",
children: []
}
]
}
]
}
There is a way, which I think can solve it, where I can loop through all the children unless there is no children upto maximum 2 levels, but that way I will miss valid children which are having depth more than 3.
I hope my question is clear. If not please let me know I will try to expand this further.
A recursive function solves this:
function check(stack,parent, obj){
stack = stack || []; //stack contains a list of all previously occurred names
var found = stack.find(function(parent){
return (parent==obj.name && obj.children.length>0); //checks to see if the current object name matches any in the stack.
});
if(!found && obj.children.length>0){
stack.push(obj.name); //adds the current object name to the list.
obj.children.forEach(function(child){
check(stack,obj, child);//recursively checks for all children.
})
}
else if(found){
parent.children=[];
parent.circular=true;
stack.pop(obj.name);
return;
}
else{
return;
}
}
check([],temp1, temp1)
This leads to alteration of the original object passed.
Hope this helps!
use console.table(circularObj) to help you in debugging

javascript (reactJS) best way to access values in a child Array

I've got the following array:
This is an array of Users, and each User has an Attributes array.
Now I want to make a new array with users and only their attributes. Like this
users{
0: {
"phone_number",
"email"
}
}
What would be the best way to achieve this?
thanks
If you need Array of users which contains arrays with users attributes, then you can use Array.prototype.map method:
let users = [
{ Attributes: [ { Name: 'phone_number' }, { Name: 'email' } ] },
{ Attributes: [ { Name: 'phone_number1' }, { Name: 'email1' } ] }
];
let result = users.map((user) => user.Attributes.map((attr) => attr.Name));
console.log(result)

Categories

Resources