How to remove a common property from each array which is nested - javascript

I have an array like below: I want to remove origin: 0 property and add it value directly using Javascript es6 feature. How to remove same repeated property from a nested array.
const orginalData = {
name: {
origin: 0,
value: 'christi'
},
location: {
origin: 0,
value: 'Blr'
},
address: {
origin: 0,
value: [{
"streetAddress1": {
"origin": 0,
"value": '12th street'
},
"city1": {
"origin": 0,
"value": 'Maxwell'
}
},
{
"streetAddress2": {
"origin": 0,
"value": '10=]]]]]]]th street'
},
"city2": {
"origin": 0,
"value": 'Coxwell'
}
}
]
}
}
const finalData = {
name: 'christi',
location: 'Blr',
address: [{
streetAddress1: '10th street',
city1: 'Maxwell'
},
{
streetAddress2: '12th street',
city2: 'Coxwell'
}
]
}

You could create generic function like this. reduce the entries of an object to remove a level of nesting and update with nested value. If value as an array, recursively call the function on each object using map and get an array of restructured objects. This will work for any level of nesting
const orginalData={name:{origin:0,value:"christi"},location:{origin:0,value:"Blr"},address:{origin:0,value:[{streetAddress1:{origin:0,value:"12th street"},city1:{origin:0,value:"Maxwell"}},{streetAddress2:{origin:0,value:"10=]]]]]]]th street"},city2:{origin:0,value:"Coxwell"}}]}};
function restructure(obj) {
return Object.entries(obj).reduce((acc, [k, { value }]) => {
acc[k] = Array.isArray(value) ? value.map(restructure) : value;
return acc;
}, {})
}
const finalData = restructure(orginalData)
console.log(finalData)

If you're using NodeJs you could use omit-deep to remove any property you want, regardless of where it is within the object.
For example, this:
const omitDeep = require('omit-deep');
const data = {
name: { origin: 0, value: 'christi' },
location: { origin: 0, value: 'Blr' },
address: {
origin: 0,
value: [
{ streetAddress1: { origin: 0, value: '12th street' }, city1: { origin: 0, value: 'Maxwell' } },
{ streetAddress2: { origin: 0, value: '10=]]]]]]]th street' }, city2: { origin: 0, value: 'Coxwell' } }
]
}
};
const finalData = omitDeep(data, 'origin');
Produces this result:
{
name: { value: 'christi' },
location: { value: 'Blr' },
address: {
value: [
{ streetAddress1: { value: '12th street' }, city1: { value: 'Maxwell' } },
{ streetAddress2: { value: '10=]]]]]]]th street' }, city2: { value: 'Coxwell' } }
]
}
};

first if you want to edit your data, it can't be a const, so change const by let or var.
second
you can use for loop to do that, you can juste write a function or add a function to JSON object
// first logique as global function
function keepKey(data, keep) {
for(let key in data) data[key] = data[key][keep];
}
// second logique as global function
function removeKey(data, remove, assignRest) {
for(let key in data){
//get the item
let item = data[key];
if(typeof item === 'object'){
//if you put 'use strict' at the top you have to use a loop
let temp = {}, lastKey = '';
for(let itemKey in item){
if(itemKey !== remove){
if(assignRest === true) temp = item[itemKey];
else temp[itemKey] = item[itemKey];
}
}
data[key] = temp;
//else you can use directly delete
//delete item[remove];
}
}
}
// add the function to JSON object
JSON.keepKey = {...function...}
// or
JSON.removeKey = {...function...}
JSON.keepKey(orginalData, 'value');
// will give you
{name: 'christi',location: 'Blr',...}
JSON.removeKey(orginalData, 'value', true);
// will give you
{name: 'christi',location: 'Blr',...}
JSON.removeKey(orginalData, 'value', false);
// will give you
{name: {value : 'christi'},location: {value: 'Blr'},...}

const finalData = {
// ...originalData, uncommnent this if you have more originalData has props that you do not want to chnage.
name: originalData.name.value,
location: originalData.location.value,
address: originalData.address.value.map(item => {
const { origin, ...rest } = item;
return rest;
}),
};

This is just a copy of adiga's logic, made more explicit with extraneous identifiers and comments.
It's intended to help with understanding of the reduce method (and other JavaScript features) and of recursion.
const originalData = {
name: { origin: 0, value: 'christi' },
location: { origin: 0, value: 'Blr' },
address: {
origin: 0,
value: [
{
"streetAddress1": { "origin": 0, "value": '12th street' },
"city1": { "origin": 0, "value": 'Maxwell' }
},
{
"streetAddress2": { "origin": 0, "value": '10=]]]]]]]th street' },
"city2": { "origin": 0, "value": 'Coxwell' }
}
]
}
};
function restructure(obj) {
// Whether `restructure` is called directly or recursively, it builds and returns
// a new object.
return Object.entries(obj).reduce( (acc, curr, ind, arr ) => {
// Here, `entries` is a 2D array where each 'row' is a property and the two
// 'columns' are the property name and property value.
// We identify them explicitly below and assume that that the property value
// is an object with a subproperty called "value", which we also identify.
const propKey = curr[0], propVal = curr[1], subpropVal = propVal["value"];
// Logs the index (ie 'row' number) of the current property and its property name
//console.log(ind, propKey);
// Here, `acc` is the object we will return. We give `acc` a new property with
// the same name as the current property.
// If the "value" subproperty of the current property holds an array, the new
// property will hold an array of objects, each of which is a `restructure`d
// version of an object from the source array. (This can happen many times,
// restructuring nested objects from many nested arrays.)
// If not, the new property will have the same value as the "value" subproperty does
acc[propKey] = Array.isArray(subpropVal) ? subpropVal.map(restructure) : subpropVal;
// If this call to `restructure` was recursive, we will continue looping through
// the array we are currently processing.
// If this was the original call, we're done and log our `finalData` to the console.
return acc;
}, {})
}
const finalData = restructure(originalData);
console.log(finalData);

Related

Adding an Array's elements to last element and compare it with another Array in Javascript

I am having two array with the same length and format given at the end.
assume the last element on each array is the score if either array has zero values in other elements.
Let's say we have array p1 and p2 each have 7 elements. If either p1 or p2 first 6 elements has zero value then it means the game is over and we sum up all other elements and add to last element(mail_hole) which define its score. Then compare each score to find the winner.
Here is my code:
function checkWinner(holes, status = "incomplete", winner = "none") {
const p1MainHole = holes["p1"].pop(); // check if all holes has zero stone.(Except main hole)
const p2MainHole = holes["p2"].pop(); // check if all holes has zero stone.(Except main hole)
if (holes["p1"].every((hole) => hole.value === 0)) {
const sumOfAllStone = this.countAllStone(holes, "p2", p2MainHole);
holes["p2"].push(sumOfAllStone);
holes["p1"].push(p1MainHole);
status = "complete";
} else if (holes["p2"].every((hole) => hole.value === 0)) {
const sumOfAllStone = this.countAllStone(holes, "p1", p1MainHole);
holes["p1"].push(sumOfAllStone);
holes["p2"].push(p2MainHole);
status = "complete";
} else {
holes["p1"].push(p1MainHole);
holes["p2"].push(p2MainHole);
}
if (status === "complete") {
winner = holes["p1"][holes["p1"].length - 1].value > holes["p2"][holes["p2"].length - 1].value ? "p1" : "p2";
}
return {
holes,
status,
winner
};
}
function countAllStone(holes, player, mainHole) {
for (let i = 0; i < holes[player].length; i++) {
mainHole.value += holes[player][i].value;
}
return mainHole;
}
console.log(
checkWinner({
p1: [
{
name: "hole_0",
value: 0,
},
{
name: "hole_1",
value: 0,
},
{
name: "hole_2",
value: 0,
},
{
name: "hole_3",
value: 0,
},
{
name: "hole_4",
value: 0,
},
{
name: "hole_5",
value: 0,
},
{
name: "main_hole",
value: 0,
},
],
p2: [
{
name: "hole_0",
value: 1,
},
{
name: "hole_1",
value: 1,
},
{
name: "hole_2",
value: 1,
},
{
name: "hole_3",
value: 1,
},
{
name: "hole_4",
value: 2,
},
{
name: "hole_5",
value: 0,
},
{
name: "main_hole",
value: 1,
},
],
})
);
At the end it compares each player's score(last elements) to find the winner.
I am not satisfied with the amount of code written and the efficiency of it. Any idea would be welcome, Thanks.
This may be one possible alternate solution to achieve the desired objective:
Code Sample
if (allZeroValues(p1) || allZeroValues(p2)) {
resObj.status = 'complete';
if (allZeroValues(p1)) updateTotal(p2);
else updateTotal(p1);
resObj.winner = getWinner(p1, p2);
};
Explanation
if either p1 or p2 are zero-valued (except 'main_hole'), then
set status to complete
if p1 is all zeroes, update p2's total
else, update p1's total
set winner based on the totals
There are several helper methods used which may be understood from perusing the snippet below.
Code Snippet
const checkWinner = (holes, status = "incomplete", winner = "none") => {
// first, declare few helper methods
// to get an array without the 'main_hole'
const skipMainHole = arr => ([
...arr.filter(el => el.name !== 'main_hole')
]);
// add total values except 'main_hole'
const sumValues = arr => (
skipMainHole(arr).reduce(
(tot, itm) => (tot + itm.value),
0
)
);
// check if array without 'main_hole' is all zeroes
// assumption: 'value' will always be non-negative integer
const allZeroValues = arr => (sumValues(arr) === 0);
// update 'main_hole' value
const updateTotal = arr => {
arr[arr.length - 1].value += sumValues(arr);
};
// get winner
const getWinner = (arr1, arr2) => (
arr1.slice(-1)[0].value === arr2.slice(-1)[0].value
? 'none'
: arr1.slice(-1)[0].value > arr2.slice(-1)[0].value
? 'p1'
: 'p2'
);
// now, de-structure holes to get the p1, p2 arrays
const {p1, p2} = holes;
// set-up a result-object
const resObj = {status, winner};
// now, for the actual logic
if (allZeroValues(p1) || allZeroValues(p2)) {
resObj.status = 'complete';
if (allZeroValues(p1)) updateTotal(p2);
else updateTotal(p1);
resObj.winner = getWinner(p1, p2);
};
// finally, return the updated result-object
return {...resObj, holes: {p1, p2}};
};
console.log(
checkWinner({
p1: [
{
name: "hole_0",
value: 0,
},
{
name: "hole_1",
value: 0,
},
{
name: "hole_2",
value: 0,
},
{
name: "hole_3",
value: 0,
},
{
name: "hole_4",
value: 0,
},
{
name: "hole_5",
value: 0,
},
{
name: "main_hole",
value: 0,
},
],
p2: [
{
name: "hole_0",
value: 1,
},
{
name: "hole_1",
value: 1,
},
{
name: "hole_2",
value: 1,
},
{
name: "hole_3",
value: 1,
},
{
name: "hole_4",
value: 2,
},
{
name: "hole_5",
value: 0,
},
{
name: "main_hole",
value: 1,
},
],
})
);

How to make the symbol.iterator resume when added a new item to the array

I print a list by iterating the Symbol.iterate object. I want it to resume printing when I add a new item to the array, but it doesn't work. I've tried some ways to do it but I couldn't. How can I overcome this problem?
let arr = [{
name : "Computer",
sold : 1000
},
{
name: "Printer",
sold: 250
},
{
name: "Camera",
sold:290
},
{
name: "Gamepad",
sold: 800
},
{
name: "Keyboard",
sold: 2100
}
]
const iteratorArr = arr[Symbol.iterator]();
const listAll = () => {
var it = iteratorArr.next()
while(!it.done) {
console.log(it.value)
it = iteratorArr.next()
}
}
listAll()
arr.push({name:"Mouse",sold: 2900})
listAll()
Array iterators (and really, any builtin iterators) do not support resumption after having been exhausted once. It is possible to create the iterator before adding values to the array, and still iterate them afterwards, but you must not have run the iterator to completion in between. Example:
const arr = ['a', 'b'];
const iter = arr.values();
console.log(iter.next().value); // not yet done
console.log(iter.next().value); // not yet done
arr.push('c');
console.log(iter.next().value); // not yet done
console.log(iter.next().done); // no value left, closing
arr.push('d');
console.log(iter.next().done); // still closed
To get the behavior you want, you'd need to implement your own iterator:
let arr = [
{ name : "Computer", sold : 1000 },
{ name: "Printer", sold: 250 },
{ name: "Camera", sold:290 },
{ name: "Gamepad", sold: 800 },
{ name: "Keyboard", sold: 2100 }
]
const iteratorArr = {
index: 0,
next() {
const done = this.index >= arr.length
return {done, value: done ? undefined : arr[this.index++]}
},
[Symbol.iterator]() { return this }
}
const listRemaining = () => {
for (const value of iteratorArr) {
console.log(value)
}
console.log(iteratorArr);
}
listRemaining()
arr.push({name:"Mouse",sold: 2900})
listRemaining()

How to invert the structure of nested array of objects in Javascript?

I currently have an array that has the following structure:
data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
But I would like to restructure the array to get something like this:
data = [
{
name: "thing1",
info: [{
time: 100,
count: 3
}, {
time: 1000,
count: 7
}, {
}]
},
{
name: "thing2",
info: [{
time: 100,
count: 2
}, {
time: 1000,
count: 0
}, {
}]
}
];
So basically the key would have to be switched from time to name, but the question is how. From other posts I have gathered that using the map function might work, but since other posts had examples to and from different structures I am still not sure how to use this.
There are a number of ways to achieve this however, the key idea will be to perform a nested looping of both data items and their (nested) info items. Doing that allows your algorithm to "visit" and "map" each piece of input data, to a corresponding value in the resulting array.
One way to express that would be to use nested calls to Array#reduce() to first obtaining a mapping of:
name -> {time,count}
That resulting mapping would then be passed to a call to Object.values() to transform the values of that mapping to the required array.
The inner workings of this mapping process are summarized in the documentation below:
const data=[{time:100,info:[{name:"thing1",count:3},{name:"thing2",count:2},{}]},{time:1e3,info:[{name:"thing1",count:7},{name:"thing2",count:0},{}]}];
const result =
/* Obtain array of values from outerMap reduce result */
Object.values(
/* Iterate array of data items by reduce to obtain mapping of
info.name to { time, count} value type */
data.reduce((outerMap, item) =>
/* Iterate inner info array of current item to compound
mapping of info.name to { time, count} value types */
item.info.reduce((innerMap, infoItem) => {
if(!infoItem.name) {
return innerMap
}
/* Fetch or insert new { name, info } value for result
array */
const nameInfo = innerMap[ infoItem.name ] || {
name : infoItem.name, info : []
};
/* Add { time, count } value to info array of current
{ name, info } item */
nameInfo.info.push({ count : infoItem.count, time : item.time })
/* Compound updated nameInfo into outer mapping */
return { ...innerMap, [ infoItem.name] : nameInfo }
}, outerMap),
{})
)
console.log(result)
Hope that helps!
The approach I would take would be to use an intermediate mapping object and then create the new array from that.
const data = [{time: 100, info: [{name: "thing1", count: 3}, {name: "thing2", count: 2}, {}]}, {time: 1e3, info: [{name: "thing1", count: 7}, {name: "thing2", count: 0}, {}]} ];
const infoByName = {};
// first loop through and add entries based on the name
// in the info list of each data entry. If any info entry
// is empty ignore it
data.forEach(entry => {
if (entry.info) {
entry.info.forEach(info => {
if (info.name !== undefined) {
if (!infoByName[info.name]) {
infoByName[info.name] = [];
}
infoByName[info.name].push({
time: entry.time,
count: info.count
});
}
});
}
});
// Now build the resulting list, where name is entry
// identifier
const keys = Object.keys(infoByName);
const newData = keys.map(key => {
return {
name: key,
info: infoByName[key]
};
})
// newData is the resulting list
console.log(newData);
Well, the other guy posted a much more elegant solution, but I ground this one out, so I figured may as well post it. :)
var data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
var newArr = [];
const objInArray = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o)
return true;
}
return false;
}
const getIndex = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o) {
return i;
}
}
return false;
}
const getInfoObj = (t, c) => {
let tmpObj = {};
tmpObj.count = c;
tmpObj.time = t;
return tmpObj;
}
for (var i=0; i < data.length; i += 1) {
let t = data[i].time;
for (var p in data[i].info) {
if ("name" in data[i].info[p]) {
if (objInArray(data[i].info[p].name, newArr)) {
let idx = getIndex(data[i].info[p].name, newArr);
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newArr[idx].info.push(newInfoObj);
} else {
let newObj = {};
newObj.name = data[i].info[p].name;
let newInfo = [];
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newInfo.push(newInfoObj);
newObj.info = newInfo;
newArr.push(newObj);
}}
}
}
console.log(newArr);
try to use Object.keys() to get the key

Create JSON Array dynamically from an object

I have an object A as shown below.
var A = {
"1": [ "1_1", "1_2", "1_3" ],
"2": [ "2_1", "2_2" ]
};
Need to build a new array dynamically using js. Suppose
object A key should map to attribute text of Array AA and value should be to children as given below.
var AA = [
{
"text": "1",
"state": "open",
"children": [
{ "text": "1_1" },
{ "text": "1_2" },
{ "text": "1_3" }
]
},
{
"text": "2",
"state": "open",
"children": [
{ "text": "2_1" },
{ "text": "2_2" }
]
}
];
This is my function but its not working as expected. Could someone pls help?
function constructJSONArr() {
var A = {
"1": [ "1_1", "1_2", "1_3" ],
"2": [ "2_1", "2_2" ]
};
for (var key in A) {
var tempArr = [];
tempArr.push(key);
for (var i = 0; i < key.length; i++) {
return {
'text': key,
'state': 'closed',
'children': A[key].map(function(child) {
return {
'text': child
};
})
}
}
}
}
When you return inside a function, the function ends and returns immediately. In your case, the return inside the for loop causes the function to return the 1st key object. To solve this, you need to create the objects and push them into an arr. You can return freely inside Array.map() because each iteration invokes a function.
Fixed solution:
Iterate with for...in. Get the key. Push a new object into arr. Use the key as the text property, the state, and children. To create the children get the array from the original object by the key, and use Array.map() to generate the child objects. Return arr.
var A = {
"1": ["1_1", "1_2", "1_3"],
"2": ["2_1", "2_2"]
};
function constructJSONArr(A) {
var arr = [];
for (var key in A) {
arr.push({
text: key,
state: 'closed',
children: A[key].map(function(t) {
return {
text: t
};
})
});
}
return arr;
}
var result = constructJSONArr(A);
console.log(result);
ESNext solution
Use Object.entries() to get keys and respective values from the object A. Iterate the entries with two nested Array.map() calls. The 1st to create the outer object, and the 2nd to create the children.
const A = {
"1": ["1_1", "1_2", "1_3"],
"2": ["2_1", "2_2"]
};
const constructJSONArr = (obj) =>
Object.entries(obj).map(([text, children]) => ({
text,
state: 'closed',
children: children.map((text) => ({
text
}))
}));
var result = constructJSONArr(A);
console.log(result);
You can use Object.keys() to iterate through the object and Array.map to create the new array.
var A = {
"1": ["1_1", "1_2", "1_3"],
"2": ["2_1", "2_2"]
};
var transformed = Object.keys(A).map(key => {
return {
text: key,
state: "open",
children: A[key].map(value => {
return {
text: value
};
})
};
});
console.log(transformed);

How can I refine this JS array search function?

I have the following function:
export const functionName = (key) => {
const data = [
{
displayOrder: 0,
key: 'key-name-1',
},
{
displayOrder: 2,
key: 'key-name-2',
},
];
for (let index in data) {
return data[index].displayOrder === 0
? data[index].key
: data[0].key;
}
};
The purpose of the function is to return the key with the lowest displayOrder. This works, but is there a better more slick method to achieve this?
Secondly, how best could I create a similar version to re-order the entire array based on displayOrder?
The proper method to use when you want to extract a single thing from an array (and you can't identify the element by itself with .find) is to use .reduce:
const functionName = () => {
const data = [
{
displayOrder: 0,
key: 'key-name-1',
},
{
displayOrder: 2,
key: 'key-name-2',
},
];
const lowestDisplayObj = data.reduce((lowestObjSoFar, currentObj) => {
if (currentObj.displayOrder < lowestObjSoFar.displayOrder) return currentObj;
return lowestObjSoFar;
}, { displayOrder: Infinity, key: null });
return lowestDisplayObj.key;
};
console.log(functionName());
Note that your current argument to functionName, key, was unused in your snippet, I'm not sure what it's for.
You could also use .sort() and select the first element in the array, but that's more computationally expensive than it needs to be.
You could make use of array reduce and do something like below.
const data = [{
displayOrder: 10,
key: 'key-name-10',
},
{
displayOrder: 2,
key: 'key-name-2',
},
{
displayOrder: 1,
key: 'key-name-1 Lowest',
}
];
let a = data.reduce((prev, item) => {
if (!prev) {
return item;
}
return (item.displayOrder < prev.displayOrder) ? item : prev;
});
console.log(a.key);
const data = [
{
displayOrder: 0,
key: 'key-name-1',
},
{
displayOrder: 2,
key: 'key-name-2',
},
];
const min = Math.min.apply(null, data.map(({displayOrder}) => +displayOrder));
const obj = data.find((obj) => obj.displayOrder == min);
console.log(obj.key);

Categories