Javascript update a array data depending on key - javascript

I have a simple array and I want to update this array with the value order:"asc" and want to delete all other order key only if type == "user" and key == "country"
const items = [
{
type: "user",
values: [
{order:"asc", key:"first_name"},
{key:"last_name"},
{key:"address"},
{key:"country"},
]
},
]
My expected result is
const items = [
{
type: "user",
values: [
{key:"first_name"},
{key:"last_name"},
{key:"address"},
{order:"asc", key:"country"},
]
},
]
I'm able to do this with map inside map. Is it possible without looping twice?
items.map(
x => { if (x.type == "user") {
x.values = x.values.map(y => {
if (y.key.includes("country")) {
y.order = "asc"
} else if (JSON.stringify(x.values).includes("country")) {
delete y.order
}
return y
})
}
return [x]
});

I think that you can do that only with double loop. I write this script but it similar to yours.
var newItems = items.map(el => {
if(el.type === "user"){
el.values = el.values.map(value => {
if(value.key === "country"){
value["order"] = "asc"
}else if(value["order"] != undefined){
delete value["order"]
}
return value
})
return el
}
})

Logic
Loop through items array.
Find nodes with type "type"
From that nodes loop through values array.
Clear "order" of nodes where key is not "country"
Add "order" as "asc" where key is "country"
Working Fiddle
const items = [
{
type: "user",
values: [
{ order: "asc", key: "first_name" },
{ key: "last_name" },
{ key: "address" },
{ key: "country" },
]
},
];
items.filter(item => item.type === "user").forEach(item => {
item.values.filter(value => value.order === "asc").forEach(value => value.order && value.key !== "country" ? delete value.order : {});
item.values.filter(value => value.key === "country").forEach(value => value.order = "asc");
});
console.log(items);

items.filter(itm => itm.type == "user")
.map(u => {u.values.map(v => {
delete v.order
if (u.values.key == "country")
u.values.order = "asc"
})
}
);

If you don't want to delete order:"asc" if there is no country in the values array then instead of JSON.stringify(x.values).includes("country") upon every object in values you could use .find() and then only loop and delete if there is an object with key:"country":
const items = [ { type: "user", values: [ {order:"asc", key:"first_name"}, {key:"last_name"}, {key:"address"}, {key:"country"}, ] }, ]
const result = items.map(obj => {
if (obj.type === 'user') {
const country = obj.values.find(o => o.key === 'country')
if (country) {
obj.values.forEach(value => delete value.order)
country.order = 'asc'
}
}
return obj
})
console.log(result)
.as-console-wrapper{min-height:100%}

Related

converting object of arrays to an array of objects in a desired format

const getSelectedItemsIds = selectedItemsList => {
let keys = Object.keys(selectedItemsList);
let selectedItems = [];
keys.map(k => {
selectedItemsList[k].map(id => {
if (k.includes("projectIds")) {
return selectedItems.push({ type: "PROJECT", id });
} else if (k.includes("subjectGroupIds")) {
return selectedItems.push({
type: "SUBJECT_GROUP",
id
});
} else if (k.includes("subjectIds")) {
return selectedItems.push({ type: "SUBJECT", id });
}
});
});
return selectedItems;
};
I have written my custom logic to get the desired result, if anyone can validate and tell me if there's a better way to do it.
I'm adding input and expected out below:
I/P:
{
projectIds: [2]
subjectGroupIds: [] // incase multiple subjects are grouped togehter
subjectIds: [4]
}
Expected format:
[{"type":"PROJECT","id":2},{"type":"SUBJECT","id":4}]
Thanks in advance
let inputData = { projectIds: [2], subjectGroupIds: [], subjectIds: [4] };
const selectedItems = this.getSelectedItemsIds(inputData);
//Function Definition
getSelectedItemsIds = selectedItemsList => {
let selectedItems = [];
let keys = Object.keys(selectedItemsList)
for ( let k of Object.keys(selectedItemsList)){
keys.forEach(key =>{
if(key === k && selectedItemsList[k].length > 0){
if(key === 'projectIds'){
selectedItems.push({ type: "PROJECT", id: selectedItemsList[k][0]})
}
else if(key === 'subjectGroupIds'){
selectedItems.push({ type: "SUBJECTGROUP", id: selectedItemsList[k][0]})
}
else if(key === 'subjectIds'){
selectedItems.push({ type: "SUBJECT", id: selectedItemsList[k][0]})
}
}
})
}
return selectedItems
}
console.log(selectedItems) //[{type: "PROJECT", id: 2}, {type: "SUBJECT", id: 4}]

Setting state of an object property inside array of objects based on a computation after match : reactjs

https://codesandbox.io/s/polished-bird-662mh?from-embed
I am having this an array of objects and an object with the following structure
this.state = {
arr : [ {
id: "val1",
initialVal: "test1",
finalVal: "final1"
},
{
id: "val2",
initialVal: "test2",
finalVal: "final2"
},
{
id: "val3",
initialVal: "test3",
finalVal: "final3"
},
{
id: "val4",
initialVal: "test4",
finalVal: "final4"
}
],
values: [ "test1","test3"]
}
this.obj = { field: "test1" , val:6}
I am writing a function that takes this obj as its parameter and based on the "field" value it should set the state of "finalVal" with the "val" property of obj with the following computation(if val is greater than 5 add "ok" to the field else add "cancel") and the objects whose property don't match in the "values" array of the state its "finalVal" should be set to just blank
So the output should look after setting state :
[
{
id: "val1",
initialVal: "test1",
finalVal: "ok"
},
{
id: "val2",
initialVal: "test2"
finalVal: "final2"
},
{
id: "val3",
initialVal: "test3"
finalVal: ""
},
{
id: "val4",
initialVal: "test4"
finalVal: "final4"
}
]
//What I have tried so far
setObjState = obj => {
let arr = [...this.state.arr];
let finalArr = arr.map(function(item) {
if (item.initialVal === obj.field) {
return { ...item, finalVal: obj.val > 5 ? "Ok" : "Cancel" };
} else {
if(this.state.values.includes(item.id){
return { ...item, finalVal: "" };
}
}
});
console.log(finalArr);
this.setState({ arr: finalArr });
}
You were missing an else condition:
setObjState = obj => {
let arr = [...this.state.arr];
let finalArr = arr.map(item => {
if (item.initialVal === obj.field) {
return { ...item,
finalVal: obj.val > 5 ? "Ok" : "Cancel"
};
} else {
if (this.state.values.includes(item.id)) {
return { ...item,
finalVal: ""
};
} else {
return { ...item
};
}
}
});
console.log(finalArr);
this.setState({
arr: finalArr
});
};
Here's a Working CodeSandbox Sample for your ref.
You can simplify that function a lot with:
setObjState = obj => {
// create a new Array of items
const arr = this.state.arr.map(item => {
// determine the value
const value = obj.val > 5 ? 'ok' : 'cancel';
// set value or empty value
const finalVal = item.initialVal === obj.field ? value : '';
// return new object with finalVal
return {
...item,
finalVal,
};
});
this.setState({ arr });
};
https://codesandbox.io/s/gifted-frost-4olqb
You need a final else block in the event that none of the if-conditions pass, just so that you can return the item as-is:
I also think you're trying to check for item.initialVal since that actually has the test numbers.
setObjState = obj => {
let arr = [...this.state.arr];
let finalArr = arr.map(item => {
if (item.initialVal === obj.field) {
return { ...item, finalVal: obj.val > 5 ? "Ok" : "Cancel" };
} else {
if (this.state.values.includes(item.initialVal)) {
return { ...item, finalVal: "" };
} else {
return item;
}
}
});
console.log(finalArr);
this.setState({ arr: finalArr });
};
See sandbox: https://codesandbox.io/s/falling-currying-no6ui

group array of objects by property first letter

im struggling a little with this, been a while since ive coded javascript ... trying to convert this
items = {
"data": [
{
"name" : "john"
},
{
"name" : "james"
},
{
"name" : "joe"
},
{
"name" : "brian"
},
{
"name" : "bojan"
},
{
"name" : "billy"
},
{
"name" : "dean"
},
{
"name" : "darren"
},
{
"name" : "doug"
}
]
}
into this format
items = {
"data": [
{
letter: "j"
names : ["john", "james", "joe"]
},
{
letter: "b"
names : ["brian", "bojan", "billy"]
},
{
letter: "j"
names : ["dean", "darren", "doug"]
},
]
}
I've been trying to do this using reduce but not having much look.... is there a simpler way to to do it?
You can use reduce to create an object with the letters as keys from which you can extrapolate the array of objects you need by iterating over the object entries using map.
const items = {"data":[{"name":"john"},{"name":"james"},{"name":"joe"},{"name":"brian"},{"name":"bojan"},{"name":"billy"},{"name":"dean"},{"name":"darren"},{"name":"doug"}]};
// `reduce` over the data to produce an object
// with letter keys, and array values where the names are added
const obj = items.data.reduce((acc, c) => {
const letter = c.name[0];
acc[letter] = (acc[letter] || []).concat(c.name);
return acc;
}, {})
// `map` over the object entries to return an array of objects
items.data = Object.entries(obj).map(([letter, names]) => {
return { letter, names }
}).sort((a, b) => a.letter > b.letter);
console.log(items);
Vanilla javascript implementation:
const items = {
"data": [
{
"name" : "john"
},
{
"name" : "james"
},
{
"name" : "joe"
},
{
"name" : "brian"
},
{
"name" : "bojan"
},
{
"name" : "billy"
},
{
"name" : "dean"
},
{
"name" : "darren"
},
{
"name" : "doug"
}
]
}
const transformed = {
data:[]
}
const findByLetter = (letter) => (element) => element.letter === letter;
for(let i = 0; i < items.data.length; i++){
const letter = items.data[i].name.split("")[0];
const elIndex = transformed.data.findIndex(findByLetter(letter));
if(elIndex > -1){
transformed.data[elIndex].names.push(items.data[i].name);
}else{
transformed.data.push({
letter,
names: [items.data[i].name],
});
}
};
console.log(transformed);
Use one reduce():
const items = {"data":[{"name":"john"},{"name":"james"},{"name":"joe"},{"name":"brian"},{"name":"bojan"},{"name":"billy"},{"name":"dean"},{"name":"darren"},{"name":"doug"}]};
let res = items.data.reduce((acc, item) => {
let l = item.name[0];
if (acc.data.filter(e => e.letter == l)[0] === undefined) acc.data.push({'letter': l, names: [] });
acc.data.filter(e => e.letter == l)[0].names.push(item.name);
return acc;
}, {"data": []})
console.log(res)

Filter selected children from data using Javascript

I try to write a function in JavaScript which filter an array by a selected property (an value).
But it works for 2 level only I do not understand what do I missing.
The data I want to filter:
var data = [
{
name: "john_pc",
children: [
{
name: "sabrina_pc",
children: [
{
name: "sabrina_pc"
},
{
name: "john_pc"
}
]
},
{
name: "john_pc"
}
]
},
{
name: "sabrina_pc"
}
]
The childrenFilter funciton :
const childrenFilter = (childrenData, filters) => {
let filteredData = childrenData.filter(item => {
for (var property in filters) {
var optionalValues = filters[property];
var value = item[property];
if (item.children) {
item.children = childrenFilter(item.children, filters);
}
let hasValue = value == optionalValues;
if (hasValue) {
return true;
}
return false;
}
return false;
}, this);
return filteredData;
}
Calling the function:
As you can see the 'childrenFilter' get an object which the key is property in the data and the key is value I want to keep.
let result = childrenFilter(data, {
"name": "a1"
});
console.log(JSON.stringify(result, null, 2))
The wanted result :
[
{
"name": "john_pc",
"children": [
{
"name": "sabrina_pc",
"children": [
{
"name": "john_pc"
}
]
},
{
"name": "john_pc"
}
]
}
]
Your filter function does not take into account whether or not children elements match the pattern, therefore even though some child elements of the object match the pattern, the object itself is being filtered out.
Here is the explanation:
{
name: "a2", // does not match filter {name:'a1} so is removed alongside child objects
children: [ // gets removed with parent object
{
name: "a2"
},
{
name: "a1"
}
]
}
This should produce the desired output:
const childrenFilter = (childrenData, filters) => {
let filteredData = childrenData.filter(item => {
for (var property in filters) {
var optionalValues = filters[property];
var value = item[property];
if (item.children) {
item.children = childrenFilter(item.children, filters);
}
let hasValue = value == optionalValues;
if (hasValue || item.children.length) { // include item when children mathes the pattern
return true;
}
return false;
}
return false;
}, this);
return filteredData;
}
You could build new array for each step of filtering, beginning from the leaves and check if this contains the wanted value.
This approach generates new objects and does not mutate the original data.
function filter(array, filters) {
return array.reduce((r, o) => {
var children = filter(o.children || [], filters);
return children || Object.entries(filters).every(([k, v]) => o[k] === v)
? (r || []).concat(Object.assign({}, o, children && { children }))
: r;
}, undefined);
}
var data = [{ name: "a1", children: [{ name: "a2", children: [{ name: "a2" }, { name: "a1" }] }, { name: "a1" }] }, { name: "b1" }];
console.log(filter(data, { name: "a1" }));
.as-console-wrapper { max-height: 100% !important; top: 0; }

javascript return property value from nested array of objects based on condition

i have an array of objects, in which each object could have an array of objects inside.
var mylist = [
{
"email" : null,
"school" : "schoolA",
"courses": [
{
"name" : 'ABC',
"type" : "chemistry"
},
{
"name" : 'XYZ',
"type": "math"
}
]
},
{
"email" : null,
"school": "schoolB"
}
];
i want to return course name if one of the course type is chemistry.
The course types are unique and even if they are some duplicates, we return the first one.
var result = mylist.some(function (el) {
el.courses && el.courses.some(function(u) {
if (u.type === 'chemistry') {
return u.name;
};
})
});
console.log('outcome:', result);
my code is not working at this stage.
The some callback should return a truthy or falsy value, which tells some whether to keep going (true = stop), and some returns a boolean, not a callback return value.
Probably simplest in this case just to assign directly to result:
var result;
mylist.some(function(el) {
return (el.courses || []).some(function(course) {
if (course.type === "chemistry") {
result = course.name;
return true;
}
return false;
});
});
Live Example:
var mylist = [
{
"email" : null,
"school" : "schoolA",
"courses": [
{
"name" : 'ABC',
"type" : "chemistry"
},
{
"name" : 'XYZ',
"type": "math"
}
]
},
{
"email" : null,
"school": "schoolB"
}
];
var result;
mylist.some(function(el) {
return (el.courses || []).some(function(course) {
if (course.type === "chemistry") {
result = course.name;
return true;
}
return false;
});
});
console.log(result);
I stuck to ES5 syntax since you didn't use any ES2015+ in your question, but in ES2015+, simplest probably to use nested for-of loops:
let result;
outer: for (const el of mylist) {
for (const course of el.courses || []) {
if (course.type === "chemistry") {
result = course.name;
break outer;
}
}
}
Live Example:
const mylist = [
{
"email" : null,
"school" : "schoolA",
"courses": [
{
"name" : 'ABC',
"type" : "chemistry"
},
{
"name" : 'XYZ',
"type": "math"
}
]
},
{
"email" : null,
"school": "schoolB"
}
];
let result;
outer: for (const el of mylist) {
for (const course of el.courses || []) {
if (course.type === "chemistry") {
result = course.name;
break outer;
}
}
}
console.log(result);
You could use reduce() method to iterate through each object in array and then find() method to find if some course matches type.
var mylist = [{"email":null,"school":"schoolA","courses":[{"name":"ABC","type":"chemistry"},{"name":"XYZ","type":"math"}]},{"email":null,"school":"schoolB"}]
const course = mylist.reduce((r, {courses}) => {
if (courses && !r) {
const course = courses.find(({type}) => type == 'chemistry');
if (course) r = course.name;
}
return r;
}, null)
console.log(course)

Categories