Which Array Method to use (JS) - javascript

I have an array,
const arr = [1, 2, 3, 4, 5, 6];
let us say I give it a condition to return numbers above 3, using the .filter() method, returns a new array.
But I need it to return the original array but making the array values that do not meet the conditions empty.
The new array should be
[ , , , , 4, 5, 6 ]
Is this possible?
Edit
Here are the changed requirements.
I have an array,
const arr = [
{ myNumber: 1, name: 'one' },
{ myNumber: 3, name: 'tree' },
{ myNumber: 6, name: 'six' },
{ myNumber: 8, name: 'eight' }
];
let us say I give it a condition to return names that include alphabet I, using the .filter() method, returns a new array.
But I need it to return the original array but making the array values that do not meet the conditions empty
The new array should be
const arr = [
{ myNumber: '', name: '' },
{ myNumber: '', name: '' },
{ myNumber: 6, name: 'six' },
{ myNumber: 8, name: 'eight' }
];
Is this possible?

To map value and modify existing array use forEach method and modify element of array.
const arr = [1, 2, 3, 4, 5, 6];
arr.forEach((ele, idx) => {
if(ele <= 3) arr[idx] = null;
});
console.log(arr);

You could delete unwanted items.
const
array = [1, 2, 3, 4, 5, 6];
for (let i = 0; i < array.length; i++) if (array[i] <= 3) delete array[i];
console.log(array);
array.forEach((v, i) => console.log(v, i));
.as-console-wrapper { max-height: 100% !important; top: 0; }

[ , , , , 4, 5, 6 ] is not possible. However, [ undefined, undefined, undefined, undefined, 4, 5, 6 ] is possible, as well as [ null, null, null, null, 4, 5, 6 ].
So, instead of using .filter(), I would use map() :
const arr = [1, 2, 3, 4, 5, 6];
const newArr = arr.map((x) => x <= 3 ? undefined : x);
console.log(newArr);
If you want the changes to be made in place, use the same logic, but in a regular loop :
const arr = [1, 2, 3, 4, 5, 6];
arr.forEach((element, index, original) => {
if (element <= 3)
original[index] = undefined;
});
console.log(arr);

This new answer is going to prove that even with entirely changed requirements a generic idea (a generic base approach) can be adapted easily to the changes but still can be recognized.
With the new requirements one does not need to change the original array itself but each item (reference) of it. Thus forEach will be the new array method of choice.
The reduce and condition based answer of the former requirements provides the main idea of working with customizable functions for e.g condition and, newly introduced with this approach, the item specific mutation via resetValues ...
function resetConditionMatchingItemValuesViaBoundConfig(item) {
const { condition, resetValues } = this; // `this` equals bound `config`.
if (condition(item)) {
// mutate an original array's item.
resetValues(item);
}
}
function isItemNumberValueLowerEqualThanThree(item) {
return (item.myNumber <= 3);
}
function resetItemValues(item) {
item.myNumber = Number.NaN;
//item.myNumber = '';
item.name = '';
}
const arr = [
{ myNumber: 1, name: 'one' },
{ myNumber: 3, name: 'tree' },
{ myNumber: 6, name: 'six' },
{ myNumber: 8, name: 'eight' }
];
console.log('before mutation ... arr :', arr);
arr.forEach(resetConditionMatchingItemValuesViaBoundConfig, {
condition: isItemNumberValueLowerEqualThanThree,
resetValues: resetItemValues,
});
console.log('after mutation ... arr :', arr);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Since the OP was asking for an array method I, as so often, want to promote Array.prototype.reduce. It proves itself to be an elegant swiss-knife again and again, ... but see yourself ...
function deleteConditionMatchingItem(condition, item, idx, arr) {
if (condition(item)) {
// mutate the original array.
delete arr[idx];
}
return condition;
}
function isLowerEqualThanThree(value) {
return (value <= 3);
}
const arr = [1, 2, 3, 4, 5, 6];
console.log('before mutation ... arr :', arr);
arr.reduce(deleteConditionMatchingItem, isLowerEqualThanThree);
console.log('after mutation ... arr :', arr);
console.log('("0" in arr ) ? ', ("0" in arr ));
console.log('("1" in arr ) ? ', ("1" in arr ));
console.log('("2" in arr ) ? ', ("2" in arr ));
console.log('("3" in arr ) ? ', ("3" in arr ));
console.log('("4" in arr ) ? ', ("4" in arr ));
console.log('("5" in arr ) ? ', ("5" in arr ));
console.log('("6" in arr ) ? ', ("6" in arr ));
.as-console-wrapper { min-height: 100%!important; top: 0; }

Related

how to move elements in nested array of objects in javascript

I saw these two questions:
Javascript move objects in a nested
array
How to move element in nested
array
But they do not work for me.
So I have a nested dynamic array like this:
const data = [
{
id: 1,
subData: [
{
id: 2,
subData: []
},
{
id: 3,
subData: [
{
id: 4,
subData: []
}
]
}
]
},
{
id: 5,
subData: []
},
.
.
.
]
I have to move the nested elements with their "id". For example, how can I write a function that gives me the following result:
const data = [
{
id: 1,
subData: [
{
id: 2,
subData: []
},
{
id: 3,
subData: [] // object with id 4 was here
}
]
},
{
id: 5,
subData: []
},
{
id: 4, // now its here
subData: []
}
.
.
.
]
What I've tried so far is to write the following function to first find an element with a specific "id" and then move that object:
const findObjById = (obj, key, value) => {
if (obj[key] === value) {
return obj;
}
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
if (obj[k] && typeof obj[k] === 'object') {
const found = findObjById(obj[k], key, value);
if (found) {
return found;
}
}
}
}
Works to find a specific object. But I could not move the found object
Here is an example using a generic traverse function which accepts a visitor callback which is run on each iteration of the traversal and based on the return value of the visitor either returns or continues. (see this answer for more discussion).
We can then create a splice_source traversal which accepts an object to traverse and a predicate to match by and returns the matched element after splicing it from its parent array, and a find_target_array which will return the subData array from an object that matches the passed predicate.
It only remains to push the retrieved source object to the retrieved target array.
This is just an example and will need error checking and streamlining for your particular use cases, but it illustrates some flexible techniques which may be useful moving forward.
const data = [{ id: 1, subData: [{ id: 2, subData: [], }, { id: 3, subData: [{ id: 4, subData: [], },], },], }, { id: 5, subData: [], },];
// generic 'traverse' which accepts a 'visitor' callback
function traverse(o, fn) {
for (const k in o) {
const res = fn.apply(this, [o, k]);
if (res) {
return res;
}
if (o[k] !== null && typeof o[k] == 'object') {
const res = traverse(o[k], fn);
if (res) return res;
}
}
}
// create custom 'visitors' to retrieve source and target arrays
const splice_source = (obj, predicate) =>
traverse(
obj,
// 'visitor' callback
(o, k) => {
let m_index = -1;
if (Array.isArray(o[k])) {
m_index = o[k].findIndex((o) => predicate(o, k));
}
return m_index !== -1 ? o[k].splice(m_index, 1)[0] : false;
});
const find_target_array = (obj, predicate) =>
traverse(
obj,
// 'visitor' callback
(o, k) => (predicate(o, k) ? o.subData : false)
);
// move {id: 4} to subData array of {id: 5}
const source_object = splice_source(data, (obj) => obj?.id === 4);
const target_array = find_target_array(data, (obj) => obj?.id === 5);
target_array.push(source_object);
console.log(JSON.stringify(data, null, 2));
// move {id: 3} to top level 'data' array
data.push(splice_source(data, (obj) => obj?.id === 3));
console.log(JSON.stringify(data, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Need to fetch a property based on iteration over an array with respect to array of objects

I have an array of objects with the following format. Each object has got few properties and also an array. I have to fetch a property if a key is present in the array.
Consider the below object: When I give key as 7 it should return me 'xyz'. If key is 3 it should give me 'abc'
[
{
val : 'abc',
arr : [1,2,3,4]
},
{
val: 'xyz',
arr : [7,8,9]
}
]
You can use find() and includes(). Use find of the main array and check if the arr of that object includes() the given key. return the val property of the found object.
const arr = [
{
val : 'abc',
arr : [1,2,3,4]
},
{
val: 'xyz',
arr : [7,8,9]
}
]
const getVal = (arr,key) => (arr.find(x => x.arr.includes(key)) || {}).val;
console.log(getVal(arr,3))
console.log(getVal(arr,7))
You can use Array.filter() to filter the object which meets the condition whereby the element (7) exists in the array. Within the callback function for Array.some(), you can use Array.includes() to check if the element (7), exists in the arr property itself:
const data = [
{
val : 'abc',
arr : [1,2,3,4]
},
{
val: 'xyz',
arr : [7,8,9]
}
]
const res = data.filter(obj => obj.arr.includes(7))[0].val;
console.log(res);
You can try this too.
var x = [{
val: 'abc',
arr: [1, 2, 3, 4]
},
{
val: 'xyz',
arr: [7, 8, 9]
}
];
var test = function(data, key) {
for (let i of x) {
if (i.arr.indexOf(key) >= 0)
return i.val;
}
}
// example
console.log(test(x,9));

Find all values by specific key in a deep nested object

How would I find all values by specific key in a deep nested object?
For example, if I have an object like this:
const myObj = {
id: 1,
children: [
{
id: 2,
children: [
{
id: 3
}
]
},
{
id: 4,
children: [
{
id: 5,
children: [
{
id: 6,
children: [
{
id: 7,
}
]
}
]
}
]
},
]
}
How would I get an array of all values throughout all nests of this obj by the key of id.
Note: children is a consistent name, and id's won't exist outside of a children object.
So from the obj, I would like to produce an array like this:
const idArray = [1, 2, 3, 4, 5, 6, 7]
This is a bit late but for anyone else finding this, here is a clean, generic recursive function:
function findAllByKey(obj, keyToFind) {
return Object.entries(obj)
.reduce((acc, [key, value]) => (key === keyToFind)
? acc.concat(value)
: (typeof value === 'object')
? acc.concat(findAllByKey(value, keyToFind))
: acc
, [])
}
// USAGE
findAllByKey(myObj, 'id')
You could make a recursive function like this:
idArray = []
function func(obj) {
idArray.push(obj.id)
if (!obj.children) {
return
}
obj.children.forEach(child => func(child))
}
Snippet for your sample:
const myObj = {
id: 1,
children: [{
id: 2,
children: [{
id: 3
}]
},
{
id: 4,
children: [{
id: 5,
children: [{
id: 6,
children: [{
id: 7,
}]
}]
}]
},
]
}
idArray = []
function func(obj) {
idArray.push(obj.id)
if (!obj.children) {
return
}
obj.children.forEach(child => func(child))
}
func(myObj)
console.log(idArray)
I found steve's answer to be most suited for my needs in extrapolating this out and creating a general recursive function. That said, I encountered issues when dealing with nulls and undefined values, so I extended the condition to accommodate for this. This approach uses:
Array.reduce() - It uses an accumulator function which appends the value's onto the result array. It also splits each object into it's key:value pair which allows you to take the following steps:
Have you've found the key? If so, add it to the array;
If not, have I found an object with values? If so, the key is possibly within there. Keep digging by calling the function on this object and append the result onto the result array; and
Finally, if this is not an object, return the result array unchanged.
Hope it helps!
const myObj = {
id: 1,
children: [{
id: 2,
children: [{
id: 3
}]
},
{
id: 4,
children: [{
id: 5,
children: [{
id: 6,
children: [{
id: 7,
}]
}]
}]
},
]
}
function findAllByKey(obj, keyToFind) {
return Object.entries(obj)
.reduce((acc, [key, value]) => (key === keyToFind)
? acc.concat(value)
: (typeof value === 'object' && value)
? acc.concat(findAllByKey(value, keyToFind))
: acc
, []) || [];
}
const ids = findAllByKey(myObj, 'id');
console.log(ids)
You can make a generic recursive function that works with any property and any object.
This uses Object.entries(), Object.keys(), Array.reduce(), Array.isArray(), Array.map() and Array.flat().
The stopping condition is when the object passed in is empty:
const myObj = {
id: 1,
anyProp: [{
id: 2,
thing: { a: 1, id: 10 },
children: [{ id: 3 }]
}, {
id: 4,
children: [{
id: 5,
children: [{
id: 6,
children: [{ id: 7 }]
}]
}]
}]
};
const getValues = prop => obj => {
if (!Object.keys(obj).length) { return []; }
return Object.entries(obj).reduce((acc, [key, val]) => {
if (key === prop) {
acc.push(val);
} else {
acc.push(Array.isArray(val) ? val.map(getIds).flat() : getIds(val));
}
return acc.flat();
}, []);
}
const getIds = getValues('id');
console.log(getIds(myObj));
Note: children is a consistent name, and id's wont exist outside
of a children object.
So from the obj, I would like to produce an array like this:
const idArray = [1, 2, 3, 4, 5, 6, 7]
Given that the question does not contain any restrictions on how the output is derived from the input and that the input is consistent, where the value of property "id" is a digit and id property is defined only within "children" property, save for case of the first "id" in the object, the input JavaScript plain object can be converted to a JSON string using JSON.stringify(), RegExp /"id":\d+/g matches the "id" property and one or more digit characters following the property name, which is then mapped to .match() the digit portion of the previous match using Regexp \d+ and convert the array value to a JavaScript number using addition operator +
const myObject = {"id":1,"children":[{"id":2,"children":[{"id":3}]},{"id":4,"children":[{"id":5,"children":[{"id":6,"children":[{"id":7}]}]}]}]};
let res = JSON.stringify(myObject).match(/"id":\d+/g).map(m => +m.match(/\d+/));
console.log(res);
JSON.stringify() replacer function can alternatively be used to .push() the value of every "id" property name within the object to an array
const myObject = {"id":1,"children":[{"id":2,"children":[{"id":3}]},{"id":4,"children":[{"id":5,"children":[{"id":6,"children":[{"id":7}]}]}]}]};
const getPropValues = (o, prop) =>
(res => (JSON.stringify(o, (key, value) =>
(key === prop && res.push(value), value)), res))([]);
let res = getPropValues(myObject, "id");
console.log(res);
Since the property values of the input to be matched are digits, all the JavaScript object can be converted to a string and RegExp \D can be used to replace all characters that are not digits, spread resulting string to array, and .map() digits to JavaScript numbers
let res = [...JSON.stringify(myObj).replace(/\D/g,"")].map(Number)
Using recursion.
const myObj = { id: 1, children: [ { id: 2, children: [ { id: 3 } ] }, { id: 4, children: [ { id: 5, children: [ { id: 6, children: [ { id: 7, } ] } ] } ] }, ]},
loop = (array, key, obj) => {
if (!obj.children) return;
obj.children.forEach(c => {
if (c[key]) array.push(c[key]); // is not present, skip!
loop(array, key, c);
});
},
arr = myObj["id"] ? [myObj["id"]] : [];
loop(arr, "id", myObj);
console.log(arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can make a recursive function with Object.entries like so:
const myObj = {
id: 1,
children: [{
id: 2,
children: [{
id: 3
}]
},
{
id: 4,
children: [{
id: 5,
children: [{
id: 6,
children: [{
id: 7,
}]
}]
}]
},
]
};
function findIds(obj) {
const entries = Object.entries(obj);
let result = entries.map(e => {
if (e[0] == "children") {
return e[1].map(child => findIds(child));
} else {
return e[1];
}
});
function flatten(arr, flat = []) {
for (let i = 0, length = arr.length; i < length; i++) {
const value = arr[i];
if (Array.isArray(value)) {
flatten(value, flat);
} else {
flat.push(value);
}
}
return flat;
}
return flatten(result);
}
var ids = findIds(myObj);
console.log(ids);
Flattening function from this answer
ES5 syntax:
var myObj = {
id: 1,
children: [{
id: 2,
children: [{
id: 3
}]
},
{
id: 4,
children: [{
id: 5,
children: [{
id: 6,
children: [{
id: 7,
}]
}]
}]
},
]
};
function findIds(obj) {
const entries = Object.entries(obj);
let result = entries.map(function(e) {
if (e[0] == "children") {
return e[1].map(function(child) {
return findIds(child)
});
} else {
return e[1];
}
});
function flatten(arr, flat = []) {
for (let i = 0, length = arr.length; i < length; i++) {
const value = arr[i];
if (Array.isArray(value)) {
flatten(value, flat);
} else {
flat.push(value);
}
}
return flat;
}
return flatten(result);
}
var ids = findIds(myObj);
console.log(ids);
let str = JSON.stringify(myObj);
let array = str.match(/\d+/g).map(v => v * 1);
console.log(array); // [1, 2, 3, 4, 5, 6, 7]
We use object-scan for a lot of our data processing needs now. It makes the code much more maintainable, but does take a moment to wrap your head around. Here is how you could use it to answer your question
// const objectScan = require('object-scan');
const find = (data, needle) => objectScan([needle], { rtn: 'value' })(data);
const myObj = { id: 1, children: [{ id: 2, children: [ { id: 3 } ] }, { id: 4, children: [ { id: 5, children: [ { id: 6, children: [ { id: 7 } ] } ] } ] }] };
console.log(find(myObj, '**.id'));
// => [ 7, 6, 5, 4, 3, 2, 1 ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan
import {flattenDeep} from 'lodash';
/**
* Extracts all values from an object (also nested objects)
* into a single array
*
* #param obj
* #returns
*
* #example
* const test = {
* alpha: 'foo',
* beta: {
* gamma: 'bar',
* lambda: 'baz'
* }
* }
*
* objectFlatten(test) // ['foo', 'bar', 'baz']
*/
export function objectFlatten(obj: {}) {
const result = [];
for (const prop in obj) {
const value = obj[prop];
if (typeof value === 'object') {
result.push(objectFlatten(value));
} else {
result.push(value);
}
}
return flattenDeep(result);
}
Below solution is generic which will return all values by matching nested keys as well e.g for below json object
{
"a":1,
"b":{
"a":{
"a":"red"
}
},
"c":{
"d":2
}
}
to find all values matching key "a" output should be return
[1,{a:"red"},"red"]
const findkey = (obj, key) => {
let arr = [];
if (isPrimitive(obj)) return obj;
for (let [k, val] of Object.entries(obj)) {
if (k === key) arr.push(val);
if (!isPrimitive(val)) arr = [...arr, ...findkey(val, key)];
}
return arr;
};
const isPrimitive = (val) => {
return val !== Object(val);
};

how do i convert array of one object containing many object inside to array of object

I have an array looks like below...
arr = [{
Bildbearbeiter: 1,
Bügler: 2,
Einkäufer: 3,
Empfänger: 4,
Fotograf: 5,
}];
but I want to change it
arr = [{
Bildbearbeiter: 1
},
{
Bügler: 2
},
{
Einkäufer: 3
},
{
Empfänger: 4
},
{
Fotograf: 5
}
];
So that i can iterate with each object. Is there any way to solve it??
Thanks in advance
Use Array.map and Object.entries
let arr = [{Bildbearbeiter: 1,Bügler: 2,Einkäufer: 3,Empfänger: 4,Fotograf: 5}];
arr = Object.entries(arr[0]).map(([k,v]) => ({[k]:v}));
console.log(arr);
You can simply use Array.map() and Object.keys():
let arr = [ { Bildbearbeiter: 1, Bügler: 2, Einkäufer: 3, Empfänger: 4, Fotograf: 5, } ];
let result = Object.keys(arr[0]).map((key)=> ({[key] : arr[0][key]}));
console.log(result);
Try this by clicking on Run code snippet button:
var arr = [{
Bildbearbeiter: 1,
Bügler: 2,
Einkäufer: 3,
Empfänger: 4,
Fotograf: 5,
}];
var newArray = [];
for (let key in arr[0]) {
newArray.push({
[key]: arr[0][key]
});
}
console.log(newArray);
Or try this:
var arr = [{
Bildbearbeiter: 1,
Bügler: 2,
Einkäufer: 3,
Empfänger: 4,
Fotograf: 5,
}];
var newArray = Object.keys(arr[0]).map(key => ({
[key]: arr[0][key]
}))
console.log(newArray);
You could reduce the array by adding all objects to the result set.
var array = [{ "Bildbearbeiter": 1, "Bügler": 2, "Einkäufer": 3, "Empfänger": 4, "Fotograf": 5 }],
result = array.reduce((r, o) => r.concat(Object.entries(o).map(([k, v]) => ({ [k]: v }))), []);
console.log(result);
You can map the array, loop through the keys of each object, and assign a brand new object holding the key and the value of the current item.
arr.map(i => {
return Object.keys(i).map(k => {
return {[k]: i[k]};
});
});
Working fiddle: http://jsfiddle.net/sw3Lxm7f/

How to check if an id (inside a list of ids) exists in an array of objects with ids?

List1:
[1,2,3,4]
List2:
[{id:1,name:hi},{id:3,name:hi},{id:5,name:hi}]
How can I check which items from List1 is missing from List2?
You can do something like this with help of map() and filter()
var list1 = [1, 2, 3, 4],
list2 = [{
id: 1,
name: 'hi'
}, {
id: 3,
name: 'hi'
}, {
id: 5,
name: 'hi'
}];
// get array of id's
var ids = list2.map(function(v) {
return v.id;
})
// get missing elements
var miss = list1.filter(function(v) {
// check element in id's array
return ids.indexOf(v) == -1;
});
document.write('<pre>' + JSON.stringify(miss, null, 3) + '</pre>');
Using ES6 arrow function
var list1 = [1, 2, 3, 4],
list2 = [{
id: 1,
name: 'hi'
}, {
id: 3,
name: 'hi'
}, {
id: 5,
name: 'hi'
}];
// get array of id's
var ids = list2.map(v => v.id);
// get missing elements
var miss = list1.filter(v => ids.indexOf(v) == -1);
document.write('<pre>' + JSON.stringify(miss, null, 3) + '</pre>');
Use Array.prototype.filter
var list1 = [1,2,3,4];
var list2 = [{id:1,name:'hi'},{id:3,name:'hi'},{id:5,name:'hi'}];
var t = list2.map(e => e.id); // get all 'ids' from 'list2'
var result = list1.filter(e => t.indexOf(e) == -1);
document.write(JSON.stringify(result));
I would try to reduce list2 to array of missing ids. Maybe like this:
var data = [{id: 1, name: 'hi'}, {id: 3, name: 'hi'}, {id: 5, name: 'hi'}]
var ids = [1, 2, 3, 4]
var missing = data.reduce(function(prev, curr) {
return prev.filter(function(id) { return id !== curr.id })
}, ids.slice())
document.write(missing)
A solution with linear complexity for sorted arrays.
var list1 = [1, 2, 4, 30],
list2 = [{ id: 1, name: 'hi' }, { id: 3, name: 'hi' }, { id: 5, name: 'hi' }],
missing = list1.filter(function (a) {
while (list2[this.index] && list2[this.index].id < a) {
this.index++;
}
return !list2[this.index] || list2[this.index].id !== a;
}, { index: 0 });
document.write('<pre>' + JSON.stringify(missing, 0, 4) + '</pre>');
Combine Array methods filter and some:
var list1 = [1,2,3,4];
var list2 = [{id:1,name:'hi'},{id:3,name:'hi'},{id:5,name:'hi'}];
return list2.filter(function(o) {
return !list1.some(function(id) { return o.id === id; })
})
which yields the objects from list2 whose ids are not in list1.
The converse, as I saw many people post, is this:
return list1.filter(function(id) {
return !list2.some(function(o) { return o.id === id; });
});
which yields the ids from list1 that do not have corresponding objects in list2

Categories