Change the nested array object by passing parameter in javascript - javascript

I would like to know how to change particular field by passing parameter type in javascript
I have object obj1, obj2, if the parameter type is string/array change the value field
as shown in expected output and vice-versa
function ChangeObj(obj, str){
var result = obj.map(e=> str==="string" ? ({...e, value:[e.value]}) : ({...e,value: e.value.toString()}) )
return result;
}
var obj1 =[
{ key: "country", id:0, value: "SG"},
{ key: "city", id:1, value: "IN"}
]
var obj2 = [
{ key: "cn", id:0, value: ["TH","MY"],img:"sample.jpg"},
{ key: "list", id:1, value: ["AU"], img:"item.jpg" }
]
var output1 = this.ChangeObj(obj1, "array");
var output2 = this.ChangeObj(obj2, "string");
Expected Output:
//output 1
[
{ key: "country", id:0, value: ["SG"] },
{ key: "city", id:1, value: ["IN"] }
]
// output 2
[
{ key: "cn", id:0, value: "TH", img:"sample.jpg"},
{ key: "cn", id:0, value: "MY", img:"sample.jpg" },
{ key: "list", id:1, value: "AU", img:"item.jpg" }
]

Because you want to generate multiple values when converting an array to a string, you can't use map directly. Instead, you could use reduce and then map the object value property inside the reduce:
function ChangeObj(obj, str) {
var result = obj.reduce((c, o) => c.concat(str === "array" ? [{ ...o,
value: [o.value]
}] : o.value.map(v => ({ ...o,
value: v
}))), []);
return result;
}
var obj1 =[
{ key: "country", id:0, value: "SG"},
{ key: "city", id:1, value: "IN"}
]
var obj2 = [
{ key: "cn", id:0, value: ["TH","MY"],img:"sample.jpg"},
{ key: "list", id:1, value: ["AU"], img:"item.jpg" }
]
var output1 = this.ChangeObj(obj1, "array");
var output2 = this.ChangeObj(obj2, "string");
console.log(output1);
console.log(output2);
Note that the sense of your ternary conditional is wrong and I have corrected it to str === "array" in this code.

Two issues:
You reversed the cases of string/array: in first case you want to wrap strings in arrays, but you pass "array" as second argument, while the function performs this wrapping when that argument is "string". So either you pass the wrong argument, or else the ternary expression should have the opposite condition.
When converting array to string you are currently applying toString to the array. But that will not multiply the number of objects in the output. It will just produce a comma separated string in one single object.
You can still use map to solve that last issue, but then apply a .flat to resolve the nested array that you'll get:
obj.map(e => str !== "string"
? {...e, value: [e.value]}
: e.value.map(value => ({...e,value}) )
).flat();

Related

How to map or assign an entry to an array-item based on some of this item's conditions?

I have array of objects,
if the name is xx then push xitems to that object and
if the name is yy then push yitems to that object
Below is the code tried , and also should not use spread operator
const result = [];
var ss=arrobj.forEach(function(e){
if(e.name === 'xx'){
result.push({id: e.id, name: e.name, country:e.country, others: xitems})
}
if(e.name === 'yy'){
result.push({id: e.id, name: e.name, country:e.country, others: yitems})
}
return result;
});
var arrobj =[
{id:1, name: "xx", country: "IN"},
{id:2, name: "yy", country: "MY"},
]
xitems =[
{title: "Finance", valid: true}
]
yitems =[
{title: "Sales", valid: true}
]
Expected Output
[
{id:1, name: "xx", country: "IN",
others:[
{title: "Finance", valid: true}
]
},
{id:2, name: "yy", country: "MY",
others: [
{title: "Sales", valid: true}
]
},
]
You should use .map for this.
const arrobj = [
{ id: 1, name: "xx", country: "IN" },
{ id: 2, name: "yy", country: "MY" },
];
const xitems = [{ title: "Finance", valid: true }];
const yitems = [{ title: "Sales", valid: true }];
const result = arrobj.map((item) => {
if (item.name === "xx") {
item.others = xitems;
} else if (item.name === "yy") {
item.others = yitems;
}
return item;
});
console.log(result);
Your code works, the only issue that I identified are.
There is no need to assign var ss with arrobj.forEach. Because Array.forEach donot return a value.
No need of return result; inside Array.forEach.
Also as an improvement you can simply assign the object with key others like Object.assign({}, e, { others: xitems }), rather than returning individual key value.
Working Fiddle
const arrobj = [
{ id: 1, name: "xx", country: "IN" },
{ id: 2, name: "yy", country: "MY" },
]
const xitems = [
{ title: "Finance", valid: true }
]
const yitems = [
{ title: "Sales", valid: true }
]
const result = [];
arrobj.forEach(function (e) {
if (e.name === 'xx') {
result.push(Object.assign({}, e, { others: xitems }))
}
if (e.name === 'yy') {
result.push(Object.assign({}, e, { others: yitems }))
}
});
console.log(result)
Variables are references to an object that has a value, variables do not store values. It is pointless to try to use a variable in that manner unless you have specific parameters. If you insist on a condition then you need to identify xitems and yitems by the objects values and/or properties or by the order they came in. If you have dynamic data how would you know what xitems or yitems really is?
The example below has been made reusable as long as you meet these requirements:
Must have an array of objects as a primary parameter.
Must have at least one array of objects for each object in the primary array. If there's more the rest will be ignored.
The secondary array of objects must be in the order you want then to end up as.
The second parameter is a rest parameter (not a spread operator, although I have no idea why OP does not want to use it). This will allow us to stuff in as many object arrays as we want.
const distOther = (main, ...oAs) => {...
Next we create an array of pairs from all of the secondary arrays
let others = oAs.map(sub => ['others', sub]);
// [['others', [{...}]], [['others', [{...}]], ...]
Then we turn our attention to the primary array. We'll work our way from the inside out. .map() each object as an array of pairs by Object.entries():
main.map((obj, idx) =>
// ...
Object.entries(obj)
// ...
// [{A: 1, B: 2}, {...}] => [[['A', 1], ['B', 2]], [[...], [...]]]
Then .concat() (a spead operator would be more succinct) each array of pairs with that of the secondary array of pairs corresponding to the current index (you'll need to wrap each secondary array in another array, so the return will level off correctly):
// main.map((obj, idx) =>
// ...
// Object.entries(obj)
.concat([others[idx]])));
// [[['A', 1], ['B', 2], ['others', [{...}]], [[...], [...], ['others', [{...}]]]
Finally we'll use Object.fromEntries() to convert each array of pairs into an object.
// main.map((obj, idx) =>
Object.fromEntries(
// Object.entries(obj)
// .concat([others[idx]])));
// [{'A': 1, 'B': 2, 'others': [{...}]},...]
const objArr =[
{id:1, name: "xx", country: "IN"},
{id:2, name: "yy", country: "MY"},
];
const x =[
{title: "Finance", valid: true}
]
const y =[
{title: "Sales", valid: true}
]
const distOther = (main, ...oAs) => {
let others = oAs.map(sub => ['others', sub]);
return main.map((obj, idx) =>
Object.fromEntries(
Object.entries(obj)
.concat([others[idx]])));
};
console.log(distOther(objArr, x, y));
I would choose a map based approach as well but without the if clauses which explicitly check for expected values of the mapped item's name property.
The approach instead utilizes map's 2nd thisArg parameter which gets applied as the mapper functions this context. Such an additional object can be provided as a map/index of custom key value pairs where key equals a mapped item's name.
Thus the mapper implementation features generic code, and due to the this binding it will be provided as function statement which makes it also re-usable and, if properly named, readable / comprehensible / maintainable too.
function assignBoundNamedValueAsOthers(item) {
// the bound key value pairs.
const index = this;
// create new object and assign, according to
// `item.name`, bound named value as `others`.
return Object.assign(
{},
item,
{ others: index[item.name] ?? [] },
);
}
const arrobj = [
{ id: 1, name: "xx", country: "IN" },
{ id: 2, name: "yy", country: "MY" },
];
const xitems = [{ title: "Finance", valid: true }];
const yitems = [{ title: "Sales", valid: true }];
const result = arrobj
.map(assignBoundNamedValueAsOthers, {
// each `key` equals an expected item's `name`.
xx: xitems,
yy: yitems,
});
console.log({
result,
arrobj,
xitems,
yitems,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
As one can see, the above implementation via Object.assign creates a new object from each mapped arrobj item. Thus the original item-references remains untouched / non mutated. It does not apply for the items of xitems and yitems since both array references are directly assigned each to its newly created others property. The above log does reflect this.
In case the goal was an entirely reference free data structure one needs to slightly change the Object.assign part of assignBoundNamedValueAsOthers ...
function assignBoundNamedValueAsOthers(item) {
// the bound key value pairs.
const index = this;
// create new object and assign, according to
// `item.name`, bound named value as `others`.
return Object.assign(
{},
item, {
others: (index[item.name] ?? [])
// dereference the `others` items as well.
.map(othersItem =>
Object.assign({}, othersItem)
)
},
);
}
const arrobj = [
{ id: 1, name: "xx", country: "IN" },
{ id: 2, name: "yy", country: "MY" },
];
const xitems = [{ title: "Finance", valid: true }];
const yitems = [{ title: "Sales", valid: true }];
const result = arrobj
.map(assignBoundNamedValueAsOthers, {
// each `key` equals an expected item's `name`.
xx: xitems,
yy: yitems,
});
console.log({
result,
arrobj,
xitems,
yitems,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
In case the OP does not need to care about immutability, the entire process then changes from a map task to a forEach task, where assignBoundNamedValueAsOthers does directly change/mutate each currently processed item of arrobj, thus forEach does not return any data but always the undefined value ...
function assignBoundNamedValueAsOthers(item) {
// the bound key value pairs.
const index = this;
// mutate the original reference of the currently
// processed `item` by directly assigning, according
// to `item.name`, the bound named value as `others`.
Object.assign(
item,
{ others: index[item.name] ?? [] },
);
// no explicit return value due to
// going to be used as a `forEach` task.
}
const arrobj = [
{ id: 1, name: "xx", country: "IN" },
{ id: 2, name: "yy", country: "MY" },
];
const xitems = [{ title: "Finance", valid: true }];
const yitems = [{ title: "Sales", valid: true }];
// mutates each item of `arrobj`.
arrobj.forEach(assignBoundNamedValueAsOthers, {
// each `key` equals an expected item's `name`.
xx: xitems,
yy: yitems,
});
console.log({
arrobj,
xitems,
yitems,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

removing object from array with filters doesnt work in javascript

I am trying to remove an object from an array but it I dont know what I am doing wrong.
I have this array declared:
listA: [
{ title: 'Food', value: 'Patato' },
{ title: 'Drink', value: 'Cola' },
{ title: 'Desert', value: 'Cheesecake' },
],
I am trying to remove the object where its value is 'Cola', what I have tried is this:
this.listA.filter(x => x.value !== 'Cola');
And it returns me the same list
I want to return this:
listA: [
{ title: 'Food', value: 'Patato' },
{ title: 'Desert', value: 'Cheesecake' },
],
Your code should be filtering just fine, I think the issue here is that filter does not modify the original array, it returns a new array with the filtered results. If you want it to overwrite the original array, you'll need to say this.listA = this.listA.filter(...)
const listA = [
{ title: "Food", value: "Patato" },
{ title: "Drink", value: "Cola" },
{ title: "Desert", value: "Cheesecake" },
];
const result = listA.filter((obj) => obj.value !== 'Cola');
Looks like you need to do something like
this.listA = this.listA.filter(x => x.value !== 'Cola')
The filter method is immutable hence the original array isn't changed
As a complement to https://stackoverflow.com/a/70688107/6316468, here is what filter does under the hood (the original array this remains untouched):
var xs = [1, 2, 3, 4];
var ys = filter.call(
xs, x => x % 2
);
console.log(
"xs = [", xs.join(), "]"
);
console.log(
"ys = [", ys.join(), "]"
);
function filter(predicate) {
var xs = []; // new array
for (let x in this) {
if (predicate(x)) {
xs.push(x);
}
}
return xs;
}

Change particular key in nested array to object in javascript

If the fields key in a object is array, change the first value of arrays as a key value pair object in javascript.
var obj =
[
{ id:1, fields:["item", "2", "list"]},
{ id:2, fields:["sample", "1", "check"]}
]
function newObj(obj) {
let objFields = {};
modifiedObj.fields.forEach(field => objFields[field] = field);
modifiedObj.fields= objFields;
return modifiedObj;
}
var result = this.newObject(obj)
Expected Output
{
item: "item",
sample: "sample"
}
Try this:
var obj =
[
{ id:1, fields:["item", "2", "list"]},
{ id:2, fields:["sample", "1", "check"]}
]
function newObject(obj) {
let objFields = {};
obj.forEach(e => {
if(e.fields && e.fields.length>0)
objFields[e.fields[0]] = e.fields[0];
});
return objFields;
}
var result = this.newObject(obj);
console.log(result);
Here is a functional approach that makes use of Object.assign(), spread operator, and Array.map() to create the object you need.
const input = [
{ id: 1, fields: ["item", "2", "list"] },
{ id: 2, fields: ["sample", "1", "check"] }
];
const process = (input) => (Object.assign(...input.map(({ fields }) => (
fields.length ? { [fields[0]]: fields[0] } : {}
))));
console.log(process(input));
Your snippet was close, you just needed to clean up the variable names, and then using map makes it a bit neater too:
const obj = [
{id: 1, fields: ["item", "2", "list"]},
{id: 2, fields: ["sample", "1", "check"]}
]
function newObj(inputArray) {
let outputObject = {};
inputArray.map(item => item.fields[0])
.forEach(field => outputObject[field] = field);
return outputObject;
}
var result = newObj(obj)
console.log(result)

Convert object to array of objects using javascript

I would like to convert an object of the following format :
{ From: {"A","B","C"}, To: {"A1","B1","C1"}, value: {1,2,3} }
I wanted to convert this array:
[
{from: "A" ,to: "A1" , value: 1 },
{from: "B" ,to: "B1" , value: 2},
{from: "C" ,to: "C1" , value: 3 }
]
How I can convert it in javascript code ?
The input you have given is wrong because it contains array with key:value pairs and object without key:value. It should be
{ From: ["A","B","C"], To:["A1","B1","C1"], value: [1,2,3] }
Now to convert to desired result you can use map()
let obj = { From: ["A","B","C"], To:["A1","B1","C1"], value: [1,2,3] }
let res = obj.From.map((form,i) => {
let value = obj.value[i];
let to = obj.To[i];
return {form,to,value}
})
console.log(res)

Removing specific key-value pairs from a document/object

I have a document that looks like something like this:
{
name: "Johnny Boy",
age: 24,
hobbies: ["fencing", "chess", "raves"],
_createdAt: 2015-05-15T18:12:26.731Z,
_createdBy: "JohnnyBoy",
_id: mQW4G5yEfZtsB6pcN
}
My goal is to return everything that doesn't start with an underscore, and format it a bit differently so I will end up with this:
[
{
fieldName: "name",
value: "Johnny Boy"
},
{
fieldName: "age",
value: 24
},
{
fieldName: "hobbies",
value: ["fencing", "chess", "raves"]
}
]
My initial solution was to run it through the _.map function of the Underscore library (which has nothing to do with my wanting to remove underscores specifically...) and then using lastIndexOf to figure out which keys begin with an underscore:
var listWithoutUnderscores = _.map(myList, function(val, key) {
if (key.lastIndexOf("_", 0) === -1)
return {fieldName: key, value: val}
return null
})
However, this will literally return null instead of the fields that began with _ in the returned array:
[
...
{
fieldname: "hobbies",
value: ["fencing", "chess", "raves"]
},
null,
null,
null
]
I want to remove them completely, ideally within the map function itself, or else by chaining it through some kind of filter but I don't know which one is fastest in this case.
You can use reduce for this:
var listWithoutUnderscores = _.reduce(myList, function(list, val, key) {
if (key.lastIndexOf("_", 0) === -1){
list.push( {fieldName: key, value: val});
}
return list;
}, []);
Underscore also comes with an array method compact which will remove all falsey and null values from an array:
_.compact([0, 1, false, 2, '', null, 3]);
=> [1, 2, 3]
You could just call _.compact(array) on the array after your map.
You can use pick and pass a predicate to get the valid keys and then map across those fields:
var validKey = function(value, key){
return _.first(key) != '_';
}
var createField = function(value, key){
return {
fieldname: key,
value: value
}
}
var result = _.chain(data)
.pick(validKey)
.map(createField)
.value();
var data = {
name: "Johnny Boy",
age: 24,
hobbies: ["fencing", "chess", "raves"],
_createdAt: '2015-05-15T18:12:26.731Z',
_createdBy: "JohnnyBoy",
_id: 'mQW4G5yEfZtsB6pcN'
}
var validKey = function(value, key){
return _.first(key) != '_';
}
var createField = function(value, key){
return {
fieldname: key,
value: value
}
}
var result = _.chain(data)
.pick(validKey)
.map(createField)
.value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

Categories

Resources