How to replace an object value in an array? - javascript

I have been stumped by this looking for some direction. I have a state object:
const state = [
{type: 'Primary', number: 123456},
{type: 'Mobile', number: 789012}
]
I have a function which is giving me the updated values for the oldValue, newValue, and index.
How would I be able to replace the specific number value for example in the 'Mobile' object and return the new array?

If you have the state array index you need to change, the newValueand oldValue:
const newState = state.map((obj, i) => {
if(i === index && obj.number === oldValue) {
let newObj = { ...obj };
newObj.number = newValue;
return newObj;
}
return obj;
}

You can use array.find() to find the corresponding object and replace a specific value :
const state = [
{type: 'Primary', number: 123456},
{type: 'Mobile', number: 789012}
]
// This will find and return the FIRST item that matches, or undefined if none are found
const ObjectToChange = state.find(elem => elem.type === 'Mobile')
if (ObjectToChange != undefined)
ObjectToChange.number = 42;
console.log(state);

If you mean change the value:
const state = [
{type: 'Primary', number: 123456},
{type: 'Mobile', number: 789012}
];
state[state.findIndex(item=>item.type==='Mobile')]={type:'Mobile',number:1111}
console.log(JSON.stringify(state,null,2));

const state = [
{type: 'Primary', number: 123456},
{type: 'Mobile', number: 789012}
]
const someFunction = (state, oldValue, newValue, index) => {
let newState = state.map(e => {
if(e.type === index) e.number = newValue;
return e
})
return newState
}
const newState = someFunction(state, '', 123456, 'Mobile')
console.log(newState)

You could use Array.map to return the updated array like so:
const updateFunction = (state, oldValue, newValue, index) => {
return state.map((stateObject, i) => i === index ? ({ ...stateObj, number: newValue }) : stateObject);
}

Related

Sort array of objects by 2 conditions

I need to sort array of objects by 2 conditions
sort by value
if names are the same show them next to each other in value order
Example example: https://codesandbox.io/s/relaxed-dhawan-sfryng?file=/src/index.js
I provide a solution for it,it's a bit complexed,you can read the comment I have added
const array = [
{
name: "John",
value: 5
},
{
name: "David",
value: 6
},
{
name: "John",
value: 2
},
{
name: "Michael",
value: 4
}
];
const customSort = (data) => {
// create a new array to make elements have same name together
let newArray = data.reduce((a,v) => {
let obj = a.find(e => e.name === v.name)
if(obj){
obj.datas.push(v)
}else{
a.push({'name':v.name,'datas':[v]})
}
return a
},[])
// sort data in the new array by value
newArray.forEach(e => {
e.datas.sort((a,b) => a.value - b.value)
})
// create a value object with boolean value to avoid add duplicate element
let values = data.reduce((a,v) => {
a[v.value] = false
return a
},{})
let keys = Object.keys(values)
let result = []
for(k of keys){
// if the value has iterated,then skip it
if(values[k]){
continue
}
// find data by value
let arr = newArray.filter(e1 => e1.datas.some(e2 => e2.value == k)).flatMap(e => e.datas)
result.push(...arr)
// mark the value already added
arr.forEach(e => {
values[e.value] = true
})
}
return result
}
console.log(customSort(array))
Hope it helps:
const arr = [
{
name: "John",
value: 5
},
{
name: "David",
value: 6
},
{
name: "John",
value: 2
},
{
name: "Michael",
value: 4
}
];
const groupByItems = [];
// Get index of the item based on name in groupByItems array
const getIndex = (name) => {
let index = -1;
groupByItems.forEach((groupByItem, _index) => {
if(groupByItem.name === name) {
index = _index;
}
});
return index;
}
// Group items by their name
const groupByName = () => {
arr.forEach((item) => {
const name = item.name;
const value = item.value;
let index = getIndex(name);
//Means that the [name] has not added before, so we should add it
if(index === -1) {
groupByItems.push({
name: name,
// Hold all values of the [name]
values: [],
// Hold minValue to sort groupByItems by that
minValue: Infinity
});
index = groupByItems.length - 1;
}
// Add current value to the list of values
groupByItems[index].values.push(value);
// Update minValue
if(groupByItems[index].minValue > value) {
groupByItems[index].minValue = value;
}
});
}
groupByName();
//Sort by minValue and then return final objects
const result = groupByItems.sort((a, b) => a.minValue - b.minValue).flatMap((item) => (
item.values.sort((firstValue, secondValue) => firstValue - secondValue).map((value) => {
return {
name: item.name,
value: value
}
})
));
console.log(result);

The best way to filter down a dataSet with javascript

The idea was to query a dataset with querystring params. I only want the "records" to match only what was queried.
Dataset
{
1111:
{
Category: "Education"
Role: "Analyst"
}
2222:
{
Category: "Communications and Media"
Role: "Analyst"
}
3333:
{
Category: "Public Sector"
Role: "Something else"
}
4444:
{
Category: "Public Sector"
Role: "Something else"
}
...
}
[[Prototype]]: Object
I'm sending in qString
Category: (2) ['Communications and Media', 'Education']
Role: ['Analyst']
length: 0
[[Prototype]]: Array(0)
I'd like to loop over that and filter/reduce so I only have records that match. Sort of an and instead of an or.
dataSet is an Object of objects. Thoughts? Thanks in advance.
export const Filtered = (qStrings, dataSet) => {
const filtered = [];
Object.entries(qStrings).forEach(([field]) => {
qStrings[field].forEach((value) => {
filtered.push(
..._.filter(dataSet, (sess) => {
if (sess[field] && sess[field].toString() === value.toString()) {
return sess;
}
})
);
});
});
return _.uniq(filtered);
};
geez, I figured it out with a colleague who's way smarter than me wink Jess!
export const Filtered = (qStrings, dataSet) => {
let filtered = [];
Object.entries(qStrings).forEach(([field], idx) => {
let source = filtered;
if (idx === 0) {
source = dataSet;
}
filtered = _.filter(source, (sess) => {
return sess[field] && sess[field].includes(qStrings[field]);
});
});
return _.uniq(filtered);
};
Now to clean this up.
Not sure if this solves your problem exactly, but you can apply this logic without mutation for a much cleaner function.
export const matches = (qStrings, dataSet) =>
Object.entries(dataSet).reduce((acc, [key, value]) =>
Object.entries(value).every(([rKey, rValue]) => qStrings[rKey]?.includes(rValue))
? { ...acc, [key]: value }
: acc,
{});
This will return records 1111 and 2222 because they match one of the categories and the role in qStrings.

Angular filter by dynamic columns

I have filter function which is working just fine when I use static column name in it like:
this.listOfData = this.listOfData.filter((item: DataItem) =>
item.name.toLowerCase().indexOf(newValue.toLowerCase()) !== -1
);
PS: item.name
But I need to search in every column of the item, how can I do that?
PS: name should be dynamic.
My ListofData has this columns:
listOfData({
id:
ticket_number:
status_name:
name: // currently my function is set to this value only.
created_by_full_name:
receive_time:
response_time:
resolution_time:
})
Update
based on Allabakash answer I have final code below which returning lots of typescript error:
ngOnInit(): void {
// this listens to the input value from the service and does something on change.
this.globalSearchService.searchTerm.subscribe((newValue: string) => {
// this is where you would apply your existing filtering.
this.searchTerm = newValue;
if(newValue != null) {
this.visible = false
this.listOfData = this.listOfData.filter((item: DataItem) =>
let keys = Object.keys(item);
for (let key of keys) {
if (typeof item[key] === 'string' &&
item[key].toLowerCase().indexOf(newValue.toLowerCase()) !== -1) {
return true;
}
}
return false;
);
}
});
}
If you wanted to search on all properties dynamically, you can try something like this.
this.listOfData = this.listOfData.filter((item: DataItem) => {
let keys = Object.keys(item);
for (let key of keys) {
if (typeof item[key] === 'string' &&
item[key].toLowerCase().indexOf(newValue.toLowerCase()) !== -1) {
return true;
}
}
return false;
}
);
You can use ES6 for a one liner solution. Below is a sample using vanilla Javascript
const initialListOfData = [
{ name: 'Peter', surname: 'John'},
{ name: 'Judas', surname: 'James'},
{ name: 'Paul', surname: 'Peter'},
{ name: 'Petrover', surname: 'Junior'}
]
const searchItem = 'pet'
const listOfData = initialListOfData.filter(
item => Object.keys(item).some(prop =>
(new RegExp(searchItem.toLowerCase())).test(item[prop].toLowerCase())
))
console.log(listOfData)
For your Problem using typescript this will be
this.listOfData = initialListOfData.filter(
(item: any) => Object.keys(item).some((prop: any) =>
(new RegExp(newValue.toLowerCase())).test(item[prop].toLowerCase())
))

Object name from array value

I have array with some values :
let typeArray = ["name", "strret", "car", "type"];
And I have one object :
let formDefinitionObject = {schema:{}}
I want the formDefinitionObject object be like :
let formDefinitionObject = {
schema: {
name: {
type: 'string',
title: 'Name',
required: true
},
strret: {
type: 'string',
title: 'Strret'
},
car: {
type: 'string',
title: 'Car'
},
type: {
type: 'string',
title: 'Type'
}
}
}
I want dynamically for each item in array to be object in formDefinitionObject.schema object. For example if i add one more item in array typeArray.push('country') to automatic add this object in formDefinitionObject.schema object.
Couldn't understand how required: true would fit in. Remaining things can be done as follows
var getFormDefinition = function(arr) {
function getSchemaObj(arr) {
return arr.map(d => ({
[d]: {
type: typeof(d),
title: d
}
}))
.reduce((a, b) => ({ ...a, ...b }))
}
var schemaObj = getSchemaObj(arr)
arr.push = function(...d) {
Object.assign(schemaObj, getSchemaObj(d))
return Array.prototype.push.call(arr, ...d)
}
return ({
schema: schemaObj
})
}
var typeArray = ["name", "strret", "car", "type"];
var result = getFormDefinition(typeArray)
console.log(result)
typeArray.push('nitish')
console.log(result)
Even though you did not clarify more how a field has to be required or does any field has to be as string, here's a solution based on what you provided so far.
The next snippet does the job and it has some explanations :
let typeArray = ['name', 'strret', 'car', 'type'],
formDefinitionObject = {
schema: {}
};
/** cycle through "typeArray" and populate "formDefinitionObject.schema" **/
typeArray.forEach(el => {
let currObj = {
type: 'string', /** YOU DID NOT SPECIY HOW TO DECIDE THE TYPE **/
title: el[0].toUpperCase() + el.substring(1), /** the title with the first letter being capitalized as you provided in the question. You can just use "el" instead of "el[0].toUpperCase() + el.substring(1)" if you'd like to print as it is **/
};
el === 'name' && (currObj['required'] = true); /** YOU DID NOT SPECIY HOW TO DECIDE IF A FIELD HAS TO BE REQUIRED. I just saw only the "name" as required so I did a basic (yet a stupid) check if the current element is "name" add a required to it **/
formDefinitionObject.schema[el] = currObj; /** add the current record to the "schema" attribute **/
});
console.dir(formDefinitionObject); /** printing the result **/
I'll be here if you answer our questions in the comments section.
Til then, hope I pushed you further.
You could use Proxy on the typeArray with the set trap so each time you push new value to the proxy array you can also add new property to your schema. This way you can simulate observer pattern.
You can also create some pattern to add additional properties like required for example name:prop1:prop2 but this way value is fixed to true.
let typeArray = ["name:required", "strret", "car", "type"];
let formDefinitionObject = {
schema: {}
}
let proxyArray = new Proxy(typeArray, {
set(obj, prop, value) {
if (prop != 'length') addToSchema(formDefinitionObject.schema, value);
return Reflect.set(...arguments);
}
})
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function addToSchema(schema, prop) {
const [name, ...params] = prop.split(':');
schema[name] = {
type: 'string',
title: capitalize(name)
}
params.forEach(param => schema[name][param] = true);
return schema;
}
proxyArray.reduce(addToSchema, formDefinitionObject.schema);
proxyArray.push('email:required:isEmail');
proxyArray.push('phone');
console.log(formDefinitionObject)
Update: You could use something like this name:prop1|value:prop2 to add property value other then true but if you don't specify value default is still true
let typeArray = ["name:required", "strret", "car", "type"];
let formDefinitionObject = {
schema: {}
}
let proxyArray = new Proxy(typeArray, {
set(obj, prop, value) {
if (prop != 'length') addToSchema(formDefinitionObject.schema, value);
return Reflect.set(...arguments);
}
})
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function addToSchema(schema, prop) {
const [name, ...params] = prop.split(':');
schema[name] = {
type: 'string',
title: capitalize(name)
}
params.forEach(param => {
const [key, value] = param.split('|');
schema[name][key] = value ? value : true
});
return schema;
}
proxyArray.reduce(addToSchema, formDefinitionObject.schema);
proxyArray.push('email:required:isEmail');
proxyArray.push('phone:default|123/555-333:required');
proxyArray.push('address')
console.log(formDefinitionObject)

Iterate object keys to replace the selected one with desired keys using es6 array functions

I have a requirement to replace the available keys with the desired keys in an object for which I was trying to execute below code, which later I found out to be incorrect usage of filter for desired output. hence I need help in getting the desired results using es6 array functions.
const columns = Object.keys(someArray).filter((columnName) => {
if (someCheck === "somecheck") {
if (columnName === 'MyName') {
const newcolumnName = `Pranav`;
return newcolumnName;
} else if (columnName === 'YourName') {
const newcolumnName = `Alex`;
return newcolumnName;
}
} else {
return (columnName !== 'sometingelse') ? columnName : '';
}
}
);
Here the someArray is as below:
someArray{
abc:"djfhdjf",
xyz:"ssss",
MyName:"onename",
YourName:"somename",
sometingelse:'somevalue'
}
I am expecting columns to be:
columns{
abc:"djfhdjf",
xyz:"ssss",
Pranav:"onename",
Alex:"somename",
sometingelse:'somevalue'
}
Please suggest how can I achieve the above expected output?
Note: I dont want to use function keyword in callbacks to avoid eslint errors
You could filter the wanted keys for replacement and replace the keys by using a new key and eleting the old one.
const
object = { abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue' },
replacements = { MyName: 'Pranav', YourName: 'Alex', sometingelse: '' };
Object
.keys(object)
.filter(k => k in replacements)
.forEach(k => {
object[replacements[k]] = object[k];
delete object[k];
});
console.log(object);
For generating an object, you could map new objects and assign them to a single object.
const
object = { abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue' },
replacements = { MyName: 'Pranav', YourName: 'Alex', sometingelse: '' },
result = Object.assign(...Object
.entries(object)
.map(([k, v]) => ({ [k in replacements ? replacements[k] : k]: v }))
);
console.log(result);
const obj = {
abc: 'djfhdjf',
xyz: 'ssss',
MyName: 'onename',
YourName: 'somename',
sometingelse: 'somevalue'
};
const newObj = Object.keys(obj).reduce((acc, key) => {
if (key === 'MyName') {
acc.newMyName = obj[key];
} else if (key === 'YourName') {
acc.newYourName = obj[key];
} else {
acc[key] = obj[key];
}
return acc;
}, {});
console.log('newObj = ', newObj);
Here is my approach, a bit long solution, but its on purpose so you can see how to do it simple without too much abstraction:
const someArray = {
abc:"djfhdjf",
xyz:"ssss",
MyName:"onename",
YourName:"somename",
sometingelse:'somevalue'
}
let foo = Object.keys(someArray).map(key => {
if(key === 'MyName') {
return 'Alex'
} else if(key === 'YourName') {
key = 'Pranav'
}
return key;
})
let bar = Object.entries(someArray).map((el, i) => {
el[0] = res[i];
return el;
})
let baz = r.reduce((acc, el)=>{
acc[`${el[0]}`] = el[1];
return acc;
},{})
console.log(baz);
You could use .reduce like so. It uses a similar idea that Nina proposed by using an object to hold your replacements. Here I have used the spread syntax to add the changed key to the accumulated object, along with it's associated value.
const someArray = {abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue'},
toUse = {MyName: "Pranav", YourName: "Alex"}, // define the keys you want to change and what they should change to
res = Object.keys(someArray).reduce((acc, key) =>
({...acc, [key in toUse ? toUse[key] : key]:someArray[key]})
, {});
console.log(res);
I am running a reduce on the keys of some array starting with an empty object. The ...acc spreads out all the properties in the reduced object. ...{ [keysMap[key] || key]: obj[key] } checks if the current key is present in keysMap.If it is present,it uses that key (keysMap[key]) otherwise it just uses the keys of the existing object.(|| key).Hope that makes sense
const renameKeys = (keysMap, obj) =>
Object.keys(obj).reduce(
(acc, key) => ({
...acc,
...{ [keysMap[key] || key]: obj[key] }
}),
{}
)
const columns = renameKeys({'MyName':'Pranav','YourName':'Alex'},someArray)

Categories