Update object from objectlist with next object key [closed] - javascript

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last month.
Improve this question
Using Javascipt and have lodash installed in my project.
My input is as follows:
const fields = {
accountBalance: {
id: 1,
field: 'input',
},
totalTrades: {
id: 2,
field: 'input',
},
winRate: {
id: 3,
field: 'slider'
},
reward: {
id: 4,
field: 'input',
},
risk: {
id: 5,
field: 'slider',
}
}
My output I would like to have:
const fields = {
accountBalance: {
id: 1,
field: 'input',
nextFieldFocus: 'totalTrades'
},
totalTrades: {
id: 2,
field: 'input',
nextFieldFocus: 'reward'
},
winRate: {
id: 3,
field: 'slider'
},
reward: {
id: 4,
field: 'input',
nextFieldFocus: 'accountBalance'
},
risk: {
id: 5,
field: 'slider',
}
}
Each object the field value can be either 'input' or a 'slider'.
Each time it update the field value I call a function that update the fields (the expected output above).
Each field where a field is a input, update the nextFieldFocus with the next key of the objectlist where the field is a input. If it can't find it in the remaining order, start over at the start of the object list where the field is a input.
What I tried:
const fields = {
accountBalance: {
id: 1,
field: 'input',
},
totalTrades: {
id: 2,
field: 'input',
},
winRate: {
id: 3,
field: 'slider'
},
reward: {
id: 4,
field: 'input',
},
risk: {
id: 5,
field: 'slider',
}
}
const keys = Object.keys(fields);
for (let i = 0; i < keys.length; i++) {
const currentKey = keys[i];
if (fields[currentKey].field === 'input') {
let nextKey = keys[i + 1];
while (nextKey && fields[nextKey].field !== 'input') {
nextKey = keys[++i % keys.length];
}
fields[currentKey].nextFieldFocus = nextKey;
}
}
console.log(fields);
Problem I am facing is that if it can't find the next object where field is a input that the nextFieldFocus won't be filled. Ideal is that it start checking from the start but I don't know how.

const fields = {
accountBalance: {
id: 1,
field: 'input',
},
totalTrades: {
id: 2,
field: 'input',
},
winRate: {
id: 3,
field: 'slider'
},
reward: {
id: 4,
field: 'input',
},
risk: {
id: 5,
field: 'slider',
}
}
const keys = Object.keys(fields);
for (let i = 0; i < keys.length; i++) {
const currentKey = keys[i];
const currentObj = fields[currentKey]
if (currentObj.field === 'input') {
for (let j = 0; j < keys.length; j += 1) {
const nextKey = keys[(i + j + 1) % keys.length];
const nextObj = fields[nextKey]
if (nextObj.field !== 'input') continue
currentObj.nextFieldFocus = nextKey;
break
}
}
}
console.log(fields);

You can do this with a single iteration through the object. Just save the first "input" and the previous input object to 2 variables. Whenever an "input" field is reached, update the previous "input" with the current key. When the last key is being iterated, update the previous input with the first input key.
const fields={accountBalance:{id:1,field:"input"},totalTrades:{id:2,field:"input"},winRate:{id:3,field:"slider"},reward:{id:4,field:"input"},risk:{id:5,field:"slider"}};
let firstInput, previous
for (const key in fields) {
const current = fields[key]
if (current.field == 'input') {
if (previous) previous.nextFieldFocus = key
else firstInput = key
previous = current
}
}
// last entry
if (previous) previous.nextFieldFocus = firstInput
console.log(fields)

const keys = Object.keys(fields);
keys.forEach((field, idx) => {
if (fields[field].field !== 'input') return;
fields[field].nextFieldFocus = keys[idx + 1];
})

Related

Problem when comparing JavaScript objects in an array

I have these 2 arrays of information. I want to resolve this problem: (Given the name of an user and a required permission, return true if
the user have that permission and false otherwise.)
const groups = [
{
id: 1,
name: 'admins',
permissions: ['CREATE', 'DELETE', 'READ', 'WRITE'],
},
{
id: 2,
name: 'readers',
permissions: ['READ'],
},
{
id: 3,
name: 'writers',
permissions: ['WRITE'],
},
{
id: 4,
name: 'managers',
permissions: ['CREATE', 'DELETE'],
}
];
const users = [
{
name: 'john',
groups: [1],
},
{
name: 'mary',
groups: [2, 3],
},
{
name: 'alice',
groups: [2]
},
{
name: 'bob',
groups: [3],
},
{
name: 'eve',
groups: [2, 4],
},
];
the code I've written so far:
function userHasPermission(userName, requiredPermission) {
//looping through first array
for (i = 0; i < users.length; i++) {
if (users[i].name === userName) {
// looping through the second array
for (j = 0; j < groups.length; j++) {
//here I don't know how to resolve this comparison, because as far as I understand it, the script has to check if userName is a valid ID inside groups array?
if (users[i].groups.includes(groups[j].id) && (groups[j].permissions.includes(requiredPermission))) {
return true;
} else {
return false;
}
}
}
}
};
userHasPermission("mary", "WRITE");
I'm new to Vanilla JavaScript and I'm stuck at this point, any help will be appreciated!
function userHasPermission(userName, requiredPermission) {
//looping through first array
for (i = 0; i < users.length; i++) {
if (users[i].name === userName) {
// looping through the second array
for (j = 0; j < groups.length; j++) {
if (users[i].groups.includes(groups[j].id) && (groups[j].permissions.includes(requiredPermission))) {
return true;
} else {
continue; // don’t return false yet.
}
}
}
}
// only when you’ve checked all possibilities
// still find no match, now you can return definitely
return false;
};
Above is imperative for-loop style based on your code. I personally prefer more functional code style. For your reference:
function userHasPermission(userName, requiredPermission) {
const targetUser = users.find(user => user.name === userName);
if (!targetUser) return false;
const userGroups = groups.filter(g => targetUser.groups.includes(g.id));
const permissions = userGroups.flatMap(g => g.permissions);
return permissions.includes(requiredPermission);
}
function userHasPermission(userName, requiredPermission) {
const user = users.find(u => u.name === userName);
let result = false;
user.groups.forEach(id => {
const group = groups.find(g => g.id === id);
result = group.permissions.includes(requiredPermission);
});
return result;
};
userHasPermission("mary", "WRITE");

Create unique values from duplicates in Javascript array of objects

I have an array of duplicated objects in Javascript. I want to create an array of unique objects by adding the index of occurrence of the individual value.
This is my initial data:
const array= [
{name:"A"},
{name:"A"},
{name:"A"},
{name:"B"},
{name:"B"},
{name:"C"},
{name:"C"},
];
This is expected end result:
const array= [
{name:"A-0"},
{name:"A-1"},
{name:"A-2"},
{name:"B-0"},
{name:"B-1"},
{name:"C-0"},
{name:"C-1"},
];
I feel like this should be fairly simple, but got stuck on it for a while. Can you please advise how I'd go about this? Also if possible, I need it efficient as the array can hold up to 1000 items.
EDIT: This is my solution, but I don't feel like it's very efficient.
const array = [
{ name: "A" },
{ name: "A" },
{ name: "C" },
{ name: "B" },
{ name: "A" },
{ name: "C" },
{ name: "B" },
];
const sortedArray = _.sortBy(array, 'name');
let previousItem = {
name: '',
counter: 0
};
const indexedArray = sortedArray.map((item) => {
if (item.name === previousItem.name) {
previousItem.counter += 1;
const name = `${item.name}-${previousItem.counter}`;
return { name };
} else {
previousItem = { name: item.name, counter: 0};
return item;
}
});
Currently you are sorting it first then looping over it, which may be not the most efficient solution.
I would suggest you to map over it with a helping object.
const a = [{name:"A"},{name:"A"},{name:"A"},{name:"B"},{name:"B"},{name:"C"},{name:"C"},], o = {};
const r = a.map(({ name }) => {
typeof o[name] === 'number' ? o[name]++ : o[name] = 0;
return { name: `${name}-${o[name]}` };
});
console.log(r);
Keep a counter, and if the current name changes, reset the counter.
This version mutates the objects. Not sure if you want a copy or not. You could potentially sort the array by object name first to ensure they are in order (if that's not already an existing precondition.)
const array = [
{ name: "A" },
{ name: "A" },
{ name: "A" },
{ name: "B" },
{ name: "B" },
{ name: "C" },
{ name: "C" },
];
let name, index;
for (let i in array) {
index = array[i].name == name ? index + 1 : 0;
name = array[i].name;
array[i].name += `-${index}`;
}
console.log(array);
Another way, if you don't want to sort, and don't want to mutate any objects, is to use a map and keep track of the current index for each object.
const array = [
// NOTE: I put the items in mixed up order.
{ name: "A" },
{ name: "C" },
{ name: "A" },
{ name: "B" },
{ name: "A" },
{ name: "C" },
{ name: "B" },
];
let index = {};
let next = name => index[name] = index[name] + 1 || 0;
let result = array.map(obj => ({ ...obj, name: obj.name + '-' + next(obj.name) }));
console.log(result);

Add/Update a property in a deeply nested array of objects based on a given key and value

I've got a deeply nested array that looks like this:
const elements = [
{
type: "section",
id: "basic-section",
title: "Basic information",
children: [
{
type: "select",
label: "Entity type",
id: "entity-type",
options: [
{ value: "person", label: "Person" },
{ value: "company", label: "Company" },
{ value: "organisation", label: "Organisation" },
],
},
{
type: "group",
conditions: [
{ type: "isEqual", variable: "entity-type", value: ["person"] },
],
children: [
{ type: "text", label: "First name", id: "entity.firstName" },
{ type: "text", label: "Last name", id: "entity.lastName" },
{ type: "number", label: "Age", id: "entity.age" },
{
type: "select",
label: "Gender",
id: "entity.gender",
defaultValue: "female",
options: [
{ value: "male", label: "Male" },
{ value: "female", label: "Female" },
],
},
],
},
{
type: "group",
conditions: [
{
type: "isEqual",
variable: "entity-type",
value: ["company", "organisation"],
},
],
children: [
{ type: "text", label: "Name", id: "entity.name" },
{ type: "text", label: "Address", id: "entity.address" },
],
},
],
},
];
I'm trying to add and update a property based on a given key and value.
Example 1: Add an option to the options list of entity-type
Example 2: Update the defaultValue of entity.gender to male
My current steps to accomplish this actions are:
1) Find the element based on the id key and id value
const element = findObject(elements, 'id', 'entity-type');
function findObject(object, key, value) {
if(object.hasOwnProperty(key) && object[key] === value) {
return object;
}
for(let i = 0; i < Object.keys(object).length; i++){
if(typeof object[Object.keys(object)[i]] == "object") {
const o = findObject(object[Object.keys(object)[i]], key, value);
if(o !== null) return o;
}
}
return null;
}
2) Create new option
const newOption = { value: 'government', label: 'Government' };
3) Add the new option to the found element
const updatedElement = Object.assign({}, element, { options: [...element.options, newOption] });
4) Replace the old element with the updatedElement
const newElementsList = // Stuck
5) Update the state with the updatedElementsList
setElementsList(newElementsList);
I don't see how I can replace the original element with the updated one based on the key and value.
Can someone help me out?
This is not recommended, but you can keep track of parent. Once you find the element, update the parent data with update value. But you loose immutability.
A better approach would be found and update the same time.
const elements = [{"type":"section","id":"basic-section","title":"Basic information","children":[{"type":"select","label":"Entity type","id":"entity-type","options":[{"value":"person","label":"Person"},{"value":"company","label":"Company"},{"value":"organisation","label":"Organisation"}]},{"type":"group","conditions":[{"type":"isEqual","variable":"entity-type","value":["person"]}],"children":[{"type":"text","label":"First name","id":"entity.firstName"},{"type":"text","label":"Last name","id":"entity.lastName"},{"type":"number","label":"Age","id":"entity.age"},{"type":"select","label":"Gender","id":"entity.gender","defaultValue":"female","options":[{"value":"male","label":"Male"},{"value":"female","label":"Female"}]}]},{"type":"group","conditions":[{"type":"isEqual","variable":"entity-type","value":["company","organisation"]}],"children":[{"type":"text","label":"Name","id":"entity.name"},{"type":"text","label":"Address","id":"entity.address"}]}]}];
// console.log("%j", elements);
function findObject(element, key, value, { parent = null, index = -1 }) {
if (element.hasOwnProperty(key) && element[key] === value) {
return { element: element, parent, index };
}
let keys = Object.keys(element);
for (let i = 0; i < keys.length; i++) {
if (typeof element[keys[i]] == "object") {
const o = findObject(element[Object.keys(element)[i]], key, value, {
parent: element,
index: i,
});
if (o !== null) return o;
}
}
return { element: null };
}
const { element, parent, index } = findObject(
elements,
"id",
"entity-type",
{}
);
const newOption = { value: "government", label: "Government" };
const updatedElement = Object.assign({}, element, {
options: [...element.options, newOption],
});
if (parent && index !== -1) parent[index] = updatedElement;
console.log(JSON.stringify(elements, null, 4));

Finding objects in a nested array along with their position

I've taken the following sample from a different question. And I am able to identify the object. But I also need to find our the position of that object. For example:
var arr = [{
Id: 1,
Categories: [{
Id: 1
},
{
Id: 2
},
]
},
{
Id: 2,
Categories: [{
Id: 100
},
{
Id: 200
},
]
}
]
If I want to find the object by the Id of the Categories, I can use the following:
var matches = [];
var needle = 100; // what to look for
arr.forEach(function(e) {
matches = matches.concat(e.Categories.filter(function(c) {
return (c.Id === needle);
}));
});
However, I also need to know the position of the object in the array. For example, if we are looking for object with Id = 100, then the above code will find the object, but how do I find that it's the second object in the main array, and the first object in the Categories array?
Thanks!
Well, if every object is unique (only in one of the categories), you can simply iterate over everything.
var arr = [{
Id: 1,
Categories: [{Id: 1},{Id: 2}]
},
{
Id: 2,
Categories: [{Id: 100},{Id: 200}]
}
];
var needle = 100;
var i = 0;
var j = 0;
arr.forEach(function(c) {
c.Categories.forEach(function(e) {
if(e.Id === needle) {
console.log("Entry is in position " + i + " of the categories and in position " + j + " in its category.");
}
j++;
});
j = 0;
i++;
});
function findInArray(needle /*object*/, haystack /*array of object*/){
let out = [];
for(let i = 0; i < haystack.lenght; i++) {
if(haystack[i].property == needle.property) {
out = {pos: i, obj: haystack[i]};
}
}
return out;
}
if you need the position and have to filter over an property of the object you can use a simple for loop. in this sample your result is an array of new object because there can be more mathches than 1 on the value of the property.
i hope it helps
Iterate over the array and set index in object where match found
var categoryGroups = [{
Id : 1,
Categories : [{
Id : 1
}, {
Id : 2
},
]
}, {
Id : 2,
Categories : [{
Id : 100
}, {
Id : 200
},
]
}
]
var filterVal = [];
var needle = 100;
for (var i = 0; i < categoryGroups.length; i++) {
var subCategory = categoryGroups[i]['Categories'];
for (var j = 0; j < subCategory.length; j++) {
if (subCategory[j]['Id'] == findId) {
filterVal.push({
catIndex : i,
subCatIndex : j,
id : needle
});
}
}
}
console.log(filterVal);
Here is solution using reduce:
var arr = [{ Id: 1, Categories: [{ Id: 1 }, { Id: 2 }, ] }, { Id: 2, Categories: [{ Id: 100 }, { Id: 200 }, ] } ]
const findPositions = (id) => arr.reduce((r,c,i) => {
let indx = c.Categories.findIndex(({Id}) => Id == id)
return indx >=0 ? {mainIndex: i, categoryIndex: indx} : r
}, {})
console.log(findPositions(100)) // {mainIndex: 1, categoryIndex: 0}
console.log(findPositions(1)) // {mainIndex: 0, categoryIndex: 0}
console.log(findPositions(200)) // {mainIndex: 1, categoryIndex: 1}
console.log(findPositions(0)) // {}
Beside the given answers with fixt depth searh, you could take an recursive approach by checking the Categories property for nested structures.
function getPath(array, target) {
var path;
array.some(({ Id, Categories = [] }) => {
var temp;
if (Id === target) {
path = [Id];
return true;
}
temp = getPath(Categories, target);
if (temp) {
path = [Id, ...temp];
return true;
}
});
return path;
}
var array = [{ Id: 1, Categories: [{ Id: 1 }, { Id: 2 },] }, { Id: 2, Categories: [{ Id: 100 }, { Id: 200 }] }];
console.log(getPath(array, 100));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Splice removes more than one index

I am making my own filter system for filtering data, and I'm building the basic functionality now, such as adding and removing filters.
Adding filters works fine, but when I play around with adding and removing, sometimes splice removes more than one filter. Why is it doing that? here is my code:
var active_filters = [];
var available_filters = [
'first_name', 'last_name', 'city'
];
var filters = {
first_name: {
title: 'First Name',
is_active: false,
types: [
{
input: 'text'
},
{
input: 'select',
options: [
'asc', 'desc'
],
values: [
'Ascending', 'Descending'
]
}
],
},
last_name: {
title: 'Last Name',
is_active: false,
types: [
{
input: 'text'
},
{
input: 'select',
options: [
'asc', 'desc'
],
values: [
'Ascending', 'Descending'
]
}
],
},
city: {
title: 'City',
is_active: false,
types: [
{
input: 'text'
},
],
},
};
var id_filters = $("#filters");
$(document).ready(function () {
id_filters.html(template_filters(filters));
});
function template_filters(filters) {
var html = '<select class="select_filters" id="select_filters" onchange="add_filter();">';
html += '<option value="0">Select Filter</option>';
for (var property in filters)
{
if (filters.hasOwnProperty(property))
{
var title = filters[property].title;
var is_active = filters[property].is_active;
var types = filters[property].types;
html += '<option value="'+property+'">'+title+'</option>';
}
}
html += '</select>';
return html;
}
function template_show_filter(filter, filter_name)
{
var html = '<div id="filter-'+filter_name+'" class="div_filter">';
html += '<span>'+filter.title+' X</span>';
html += '</div>';
return html;
}
function add_filter()
{
var select_filters = $("#select_filters");
var selected_filter = select_filters.val();
if (selected_filter != 0)
{
if (active_filters.length == 0)
{
active_filters.push(selected_filter);
id_filters.append(template_show_filter(filters[selected_filter], selected_filter));
}
else
{
if (active_filters.indexOf(selected_filter) === -1)
{
active_filters.push(selected_filter);
id_filters.append(template_show_filter(filters[selected_filter], selected_filter));
}
}
}
}
function remove_filter(filter_name)
{
var index = active_filters.indexOf(filter_name);
if (index >= 0)
{
var id = $("#filter-"+filter_name);
id.remove();
active_filters.splice(index); // here, it removes more than one
}
}
Please have a look at MDN web docs – Array.prototype.splice().
If you want to remove only one item, you should call .splice(index, 1).
If you don’t specify the second argument, “then all of the elements beginning with start index on through the end of the array will be deleted.”
This is because you are splicing at index.
active_filters.splice(index);
This will remove all elements after the index value.

Categories

Resources