Related
How would you go about splitting an array at the word 'split' and creating smaller sub arrays out of them?
This is what my array looks like right now (but much longer):
const myArray = ['abc', 'xyz', 123, 'split', 'efg', 'hij', 456, 'split'];
This is what I would like it to look like:
const newArray =[['abc', 'xyz', 123], ['efg', 'hij', 456]];
If it helps at all I also have the indexes of the words 'split' in an array like this:
const splitIndex = [3, 7];
You could iterate splitIndex and slice the array until this index.
const
data = ['abc', 'xyz', 123, 'split', 'efg', 'hij', 456, 'split'],
splitIndex = [3, 7],
result = [];
let i = 0;
for (const j of splitIndex) {
result.push(data.slice(i, j));
i = j + 1;
}
console.log(result);
const myArr = ['abc', 'xyz', 123, 'split', 'efg', 'hij', 456, 'split'];
const foo = (arr, key) => {
let temp = [];
const result = [];
arr.forEach(v => {
if (v !== key) {
temp.push(v);
} else {
result.push(temp);
temp = [];
}
})
return result;
}
console.log(foo(myArr, 'split'));
output:
[ [ 'abc', 'xyz', 123 ], [ 'efg', 'hij', 456 ] ]
I have two arrays
let arr1 = [{'id': 'ee', 'seat': '12'},
{'id': 'aa', 'seat': '8'}
]
let arr2 = [
{'id': 's22', 'num': ''},
{'id': '2s2', 'num': ''}
]
I want to copy seat values from arr1 to num property at arr2, but I only get last arr1 seat value in for loop.
for( let i = 0; i <= arr1.length; i++) {
for( let x = 0; x <= arr2.length; x++) {
arr2[x]['num'] = arr1[i]['seat'];
}
}
console.log(arr2);
Iterate arr2 with Array.forEach(), and take the respective seat value by index from arr1:
const arr1 = [{'id': 'ee', 'seat': '12'},{'id': 'aa', 'seat': '8'}]
const arr2 = [{'id': 's22', 'num': ''},{'id': '2s2', 'num': ''}]
arr2.forEach((o, i) => o.num = arr1[i].seat)
console.log(arr2)
You need just a single loop and check if the index of the array if is (only) smaller than the length of the minimum of both arrays.
If the index get the length of an array, the access returns undefined, because this element is not in the array.
A further access to a property of this throws an error:
Unable to get property 'seat' of undefined or null reference
var arr1 = [{ id: 'ee', seat: '12' }, { id: 'aa', seat: '8' }],
arr2 = [{ id: 's22', num: '' }, { id: '2s2', num: '' }],
i, l;
for (i = 0, l = Math.min(arr1.length, arr2.length); i < l; i++) {
arr2[i].num = arr1[i].seat;
}
console.log(arr2);
You can do it in just one for loop.
for(let i = 0; i < arr1.length; i++) {
arr2[i].num = arr1[i].seat;
}
Hope this helps!
Assuming you want to match indices, this should do it.
const arr1 = [
{'id': 'ee', 'seat': '12'},
{'id': 'aa', 'seat': '8'}
]
const arr2 = [
{'id': 's22', 'num': ''},
{'id': '2s2', 'num': ''}
]
const result = arr2.map((e, i) => ({...e, ...{num: arr1[i].seat}}))
console.log(result)
If you want all of the seats in each num, it wouldn't be much harder.
I have a problem with picking some data with array of objects and pushing it into new object (in different array), but in different way.
const fruits = [
{name: "apple"},
{name: "kiwi"},
{name: "banana"},
{name: "orange"},
{name: "pineapple"},
{name: "coconut"},
{name: "peach"},
{name: "lemon"}
]
I want to pick a few items from this and paste them into completely new array, so it could look like this:
const manyFruits = [
{name: "apple-banana-kiwi-coconut"},
{name: "orange-pineapple-peach-lemon"}]
It would be great to do a function which takes an argument of how many fruits we want to pick into new array and they shouldn't repeat.
Below is my code. Firstly I create new array with names, then I push them into new array which depends on "length" and finally I try to create new array with objects, but it fails.
const fruitArray = length => {
const fruits = [
{name: "apple"},
{name: "kiwi"},
{name: "banana"},
{name: "orange"},
{name: "pineapple"},
{name: "coconut"},
{name: "peach"},
{name: "lemon"}
]
const allFruits = []
for (let i = 0; i < fruits.length; i++) {
allFruits.push(fruits[i].name)
}
const newFruits =[]
for (let i = 0; i < length; i++) {
newFruits.push(allFruits[i])
}
const manyFruitsInOneArr = []
for (let i = 0; i < 2; i++) {
let newArr = {
name: newFruits.join("-"),
}
manyFruitsInOneArr[i] = (newArr)
}
console.log(manyFruitsInOneArr)
}
fruitArray(2)
It generates new objects in new array, but items are the same all the time.
You can create function for this using for loop and inside map() method to get array of names and join() to make a string from values.
const fruits = [{"name":"apple"},{"name":"kiwi"},{"name":"banana"},{"name":"orange"},{"name":"pineapple"},{"name":"coconut"},{"name":"peach"},{"name":"lemon"}]
const pick = (arr, n) => {
const r = [];
for (var i = 0; i < arr.length; i += n) {
const name = arr
.slice(i, i + n)
.map(({name}) => name)
.join('-');
r.push({name})
}
return r;
}
const result = pick(fruits, 2)
console.log(result)
Update: to get names and sum of energy for each slice you can use map() and reduce() methods.
fruits = [ {name: "apple", energy: 100}, {name: "kiwi", energy: 126}, {name: "banana", energy: 150}, {name: "orange", energy: 118}, {name: "pineapple", energy: 98}, {name: "coconut", energy: 83}, {name: "peach", energy: 65}, {name: "lemon", energy: 36} ]
const pick = (arr, n) => {
const r = [];
for (var i = 0; i < arr.length; i += n) {
const slice = arr.slice(i, i+n);
const name = slice.map(({name}) => name).join('-')
const energy = slice.reduce((r, {energy}) => r + energy, 0);
r.push({name, energy})
}
return r;
}
const result = pick(fruits, 2)
console.log(result)
That could be done using recursion:
function subset(arr, n){
if(!(n - 1)) return [arr[0].name];
const result = [];
for(let i = 0; i < arr.length - n; i++){
for(const sub of subset(arr.slice(i + 1), n - 1)){
result.push(arr[i].name + "-" + sub);
}
}
return result;
}
So you can do:
subset(fruits, 5);
This question already has answers here:
How to get the difference between two arrays in JavaScript?
(84 answers)
Closed 5 months ago.
I am looking for an efficient way to remove all elements from a javascript array if they are present in another array.
// If I have this array:
var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
// and this one:
var toRemove = ['b', 'c', 'g'];
I want to operate on myArray to leave it in this state: ['a', 'd', 'e', 'f']
With jQuery, I'm using grep() and inArray(), which works well:
myArray = $.grep(myArray, function(value) {
return $.inArray(value, toRemove) < 0;
});
Is there a pure javascript way to do this without looping and splicing?
Use the Array.filter() method:
myArray = myArray.filter( function( el ) {
return toRemove.indexOf( el ) < 0;
} );
Small improvement, as browser support for Array.includes() has increased:
myArray = myArray.filter( function( el ) {
return !toRemove.includes( el );
} );
Next adaptation using arrow functions:
myArray = myArray.filter( ( el ) => !toRemove.includes( el ) );
ECMAScript 6 sets can permit faster computing of the elements of one array that aren't in the other:
const myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
const toRemove = new Set(['b', 'c', 'g']);
const difference = myArray.filter( x => !toRemove.has(x) );
console.log(difference); // ["a", "d", "e", "f"]
Since the lookup complexity for the V8 engine browsers use these days is O(1), the time complexity of the whole algorithm is O(n).
var myArray = [
{name: 'deepak', place: 'bangalore'},
{name: 'chirag', place: 'bangalore'},
{name: 'alok', place: 'berhampur'},
{name: 'chandan', place: 'mumbai'}
];
var toRemove = [
{name: 'deepak', place: 'bangalore'},
{name: 'alok', place: 'berhampur'}
];
myArray = myArray.filter(ar => !toRemove.find(rm => (rm.name === ar.name && ar.place === rm.place) ))
The filter method should do the trick:
const myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
const toRemove = ['b', 'c', 'g'];
// ES5 syntax
const filteredArray = myArray.filter(function(x) {
return toRemove.indexOf(x) < 0;
});
If your toRemove array is large, this sort of lookup pattern can be inefficient. It would be more performant to create a map so that lookups are O(1) rather than O(n).
const toRemoveMap = toRemove.reduce(
function(memo, item) {
memo[item] = memo[item] || true;
return memo;
},
{} // initialize an empty object
);
const filteredArray = myArray.filter(function (x) {
return toRemoveMap[x];
});
// or, if you want to use ES6-style arrow syntax:
const toRemoveMap = toRemove.reduce((memo, item) => ({
...memo,
[item]: true
}), {});
const filteredArray = myArray.filter(x => toRemoveMap[x]);
If you are using an array of objects. Then the below code should do the magic, where an object property will be the criteria to remove duplicate items.
In the below example, duplicates have been removed comparing name of each item.
Try this example. http://jsfiddle.net/deepak7641/zLj133rh/
var myArray = [
{name: 'deepak', place: 'bangalore'},
{name: 'chirag', place: 'bangalore'},
{name: 'alok', place: 'berhampur'},
{name: 'chandan', place: 'mumbai'}
];
var toRemove = [
{name: 'deepak', place: 'bangalore'},
{name: 'alok', place: 'berhampur'}
];
for( var i=myArray.length - 1; i>=0; i--){
for( var j=0; j<toRemove.length; j++){
if(myArray[i] && (myArray[i].name === toRemove[j].name)){
myArray.splice(i, 1);
}
}
}
alert(JSON.stringify(myArray));
Lodash has an utility function for this as well:
https://lodash.com/docs#difference
How about the simplest possible:
var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
var toRemove = ['b', 'c', 'g'];
var myArray = myArray.filter((item) => !toRemove.includes(item));
console.log(myArray)
I just implemented as:
Array.prototype.exclude = function(list){
return this.filter(function(el){return list.indexOf(el)<0;})
}
Use as:
myArray.exclude(toRemove);
You can use _.differenceBy from lodash
const myArray = [
{name: 'deepak', place: 'bangalore'},
{name: 'chirag', place: 'bangalore'},
{name: 'alok', place: 'berhampur'},
{name: 'chandan', place: 'mumbai'}
];
const toRemove = [
{name: 'deepak', place: 'bangalore'},
{name: 'alok', place: 'berhampur'}
];
const sorted = _.differenceBy(myArray, toRemove, 'name');
Example code here: CodePen
If you cannot use new ES5 stuff such filter I think you're stuck with two loops:
for( var i =myArray.length - 1; i>=0; i--){
for( var j=0; j<toRemove.length; j++){
if(myArray[i] === toRemove[j]){
myArray.splice(i, 1);
}
}
}
Now in one-liner flavor:
console.log(['a', 'b', 'c', 'd', 'e', 'f', 'g'].filter(x => !~['b', 'c', 'g'].indexOf(x)))
Might not work on old browsers.
This is pretty late but adding this to explain what #mojtaba roohi has answered. The first block of code will not work as each array is having a different object, i.e. df[0] != nfl[2]. Both objects look similar but are altogether different, which is not the case when we use primitive types like numbers.
let df = [ {'name': 'C' },{'name': 'D' }]
let nfl = [ {'name': 'A' },{'name': 'B' },{'name': 'C' },{'name': 'D' }]
let res = nfl.filter(x => df.indexOf(x)<0)
console.log(res)
Here is the working code:
let df = [{'name': 'C' },{'name': 'D' }]
let nfl = [ {'name': 'A' },{'name': 'B' },{'name': 'C' },{'name': 'D' }];
let res = nfl.filter((o1) => !df.some((o2) => o1.name === o2.name));
console.log(res)
If you're using Typescript and want to match on a single property value, this should work based on Craciun Ciprian's answer above.
You could also make this more generic by allowing non-object matching and / or multi-property value matching.
/**
*
* #param arr1 The initial array
* #param arr2 The array to remove
* #param propertyName the key of the object to match on
*/
function differenceByPropVal<T>(arr1: T[], arr2: T[], propertyName: string): T[] {
return arr1.filter(
(a: T): boolean =>
!arr2.find((b: T): boolean => b[propertyName] === a[propertyName])
);
}
Proper way to remove all elements contained in another array is to make source array same object by remove only elements:
Array.prototype.removeContained = function(array) {
var i, results;
i = this.length;
results = [];
while (i--) {
if (array.indexOf(this[i]) !== -1) {
results.push(this.splice(i, 1));
}
}
return results;
};
Or CoffeeScript equivalent:
Array.prototype.removeContained = (array) ->
i = #length
#splice i, 1 while i-- when array.indexOf(#[i]) isnt -1
Testing inside chrome dev tools:
19:33:04.447 a=1
19:33:06.354 b=2
19:33:07.615 c=3
19:33:09.981 arr = [a,b,c]
19:33:16.460 arr1 = arr
19:33:20.317 arr1 === arr
19:33:20.331 true
19:33:43.592 arr.removeContained([a,c])
19:33:52.433 arr === arr1
19:33:52.438 true
Using Angular framework is the best way to keep pointer to source object when you update collections without large amount of watchers and reloads.
I build the logic without using any built-in methods, please let me know any optimization or modifications.
I tested in JS editor it is working fine.
var myArray = [
{name: 'deepak', place: 'bangalore'},
{name: 'alok', place: 'berhampur'},
{name: 'chirag', place: 'bangalore'},
{name: 'chandan', place: 'mumbai'},
];
var toRemove = [
{name: 'chirag', place: 'bangalore'},
{name: 'deepak', place: 'bangalore'},
/*{name: 'chandan', place: 'mumbai'},*/
/*{name: 'alok', place: 'berhampur'},*/
];
var tempArr = [];
for( var i=0 ; i < myArray.length; i++){
for( var j=0; j<toRemove.length; j++){
var toRemoveObj = toRemove[j];
if(myArray[i] && (myArray[i].name === toRemove[j].name)) {
break;
}else if(myArray[i] && (myArray[i].name !== toRemove[j].name)){
var fnd = isExists(tempArr,myArray[i]);
if(!fnd){
var idx = getIdex(toRemove,myArray[i])
if (idx === -1){
tempArr.push(myArray[i]);
}
}
}
}
}
function isExists(source,item){
var isFound = false;
for( var i=0 ; i < source.length; i++){
var obj = source[i];
if(item && obj && obj.name === item.name){
isFound = true;
break;
}
}
return isFound;
}
function getIdex(toRemove,item){
var idex = -1;
for( var i=0 ; i < toRemove.length; i++){
var rObj =toRemove[i];
if(rObj && item && rObj.name === item.name){
idex=i;
break;
}
}
return idex;
}
//Using the new ES6 Syntax
console.log(["a", "b", "c", "d", "e", "f", "g"].filter(el => !["b", "c", "g"].includes(el)));
// OR
// Main array
let myArray = ["a", "b", "c", "d", "e", "f", "g"];
// Array to remove
const toRemove = ["b", "c", "g"];
const diff = () => (myArray = myArray.filter((el) => !toRemove.includes(el)));
console.log(diff()); // [ 'a', 'd', 'e', 'f' ]
// OR
const diff2 = () => {
return myArray = myArray.filter((el) => !toRemove.includes(el));
};
console.log(diff2()); // [ 'a', 'd', 'e', 'f' ]
A High performance and immutable solution
Javascript
const excludeFromArr = (arr, exclude) => {
const excludeMap = exclude.reduce((all, item) => ({ ...all, [item]: true }), {});
return arr.filter((item) => !excludeMap?.[item]);
};
Typescript:
const excludeFromArr = (arr: string[], exclude: string[]): string[] => {
const excludeMap = exclude.reduce<Record<string, boolean>>((all, item) => ({ ...all, [item]: true }), {});
return arr.filter((item) => !excludeMap?.[item]);
};
I have two JSON objects with the same structure and I want to concat them together using Javascript. Is there an easy way to do this?
Based on your description in the comments, you'd simply do an array concat:
var jsonArray1 = [{'name': "doug", 'id':5}, {'name': "dofug", 'id':23}];
var jsonArray2 = [{'name': "goud", 'id':1}, {'name': "doaaug", 'id':52}];
jsonArray1 = jsonArray1.concat(jsonArray2);
// jsonArray1 = [{'name': "doug", 'id':5}, {'name': "dofug", 'id':23},
//{'name': "goud", 'id':1}, {'name': "doaaug", 'id':52}];
If you'd rather copy the properties:
var json1 = { value1: '1', value2: '2' };
var json2 = { value2: '4', value3: '3' };
function jsonConcat(o1, o2) {
for (var key in o2) {
o1[key] = o2[key];
}
return o1;
}
var output = {};
output = jsonConcat(output, json1);
output = jsonConcat(output, json2);
Output of above code is{ value1: '1', value2: '4', value3: '3' }
The actual way is using JS Object.assign.
Object.assign(target, ...sources)
MDN Link
There is another object spread operator which is proposed for ES7 and can be used with Babel plugins.
Obj = {...sourceObj1, ...sourceObj2}
I use:
let x = { a: 1, b: 2, c: 3 }
let y = {c: 4, d: 5, e: 6 }
let z = Object.assign(x, y)
console.log(z)
// OUTPUTS:
{ a:1, b:2, c:4, d:5, e:6 }
From here.
You can use jquery extend method.
Example:
o1 = {"foo":"bar", "data":{"id":"1"}};
o2 = {"x":"y"};
sum = $.extend(o1, o2);
Result:
sum = {"foo":"bar", "data":{"id":"1"}, "x":"y"}
One solution is to use a list/array:
var first_json = {"name":"joe", "age":27};
var second_json = {"name":"james", "age":32};
var jsons = new Array();
jsons.push(first_json);
jsons.push(second_json);
Result
jsons = [
{"name":"joe", "age":27},
{"name":"james", "age":32}
]
if using TypeScript, you can use the spread operator (...)
var json = {...json1,...json2}
You can use Object.assign() method. The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.[1]
var o1 = { a: 1 }, o2 = { b: 2 }, o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
okay, you can do this in one line of code. you'll need json2.js for this (you probably already have.). the two json objects here are unparsed strings.
json1 = '[{"foo":"bar"},{"bar":"foo"},{"name":"craig"}]';
json2 = '[{"foo":"baz"},{"bar":"fob"},{"name":"george"}]';
concattedjson = JSON.stringify(JSON.parse(json1).concat(JSON.parse(json2)));
Just try this, using underscore
var json1 = [{ value1: '1', value2: '2' },{ value1: '3', value2: '4' }];
var json2 = [{ value3: 'a', value4: 'b' },{ value3: 'c', value4: 'd' }];
var resultArray = [];
json1.forEach(function(obj, index){
resultArray.push(_.extend(obj, json2[index]));
});
console.log("Result Array", resultArray);
Result
var baseArrayOfJsonObjects = [{},{}];
for (var i=0; i<arrayOfJsonObjectsFromAjax.length; i++) {
baseArrayOfJsonObjects.push(arrayOfJsonObjectsFromAjax[i]);
}
I use:
let jsonFile = {};
let schemaJson = {};
schemaJson["properties"] = {};
schemaJson["properties"]["key"] = "value";
jsonFile.concat(schemaJson);
The simplest way :
const json1 = { value1: '1', value2: '2' };
const json2 = { value2: '4', value3: '3' };
const combinedData = {
json1,
json2
};
console.log(combinedData)
I dont know if you want this:
U can use this for create from arrays, all arrays need contains the same number of elments.
Example:
If you have:
let a = ["a", "b", "c"];
let b = [1, 2, 3];
Use
concatArraysLikeJson([a, b]);
The result of is:
let result = {
0 : ["a", 1],
1 : ["b", 2],
2 : ["c", 3]
};
Typescript
concatArraysLikeJson(arrays:any){
let result:any = {};
let size:number = 0;
let make:boolean = true;
if(arrays.length > 0){
size = arrays[0].length;
for(let i = 1; i < arrays.length; i++){
let array = arrays[i];
if(make){
if(array.length != size){
make = false;
}
}
}
}
if(make){
for (let o = 0; o < size; o++) {
result[o] = [];
}
for(let i = 0; i < arrays.length; i++){
const array = arrays[i];
//console.log(array);
for (let o = 0; o < size; o++) {
const element = array[o];
result[o].push(element);
}
}
return result;
}else{
return false;
}
}
Javascript:
concatArraysLikeJson(arrays){
let result = {};
let size = 0;
let make = true;
if(arrays.length > 0){
size = arrays[0].length;
for(let i = 1; i < arrays.length; i++){
let array = arrays[i];
if(make){
if(array.length != size){
make = false;
}
}
}
}
if(make){
for (let o = 0; o < size; o++) {
result[o] = [];
}
for(let i = 0; i < arrays.length; i++){
const array = arrays[i];
//console.log(array);
for (let o = 0; o < size; o++) {
const element = array[o];
result[o].push(element);
}
}
return result;
}else{
return false;
}
}
The JSON Objects and Arrays can be combined in multiple ways within a structure
I can merge json with rules using json-object-merge
import JSONObjectMerge from "json-object-merge";
const target = {
store: {
book: [
{
category: "reference",
author: "Nigel Rees",
title: "Sayings of the Century",
price: 8.95
}
],
bicycle: {
color: "red",
price: 19.95
}
}
};
const source = {
store: {
book: [
{
category: "fiction",
author: "Evelyn Waugh",
title: "Sword of Honour",
isbn: "0-679-43136-5",
price: 12.99
}
]
}
};
const merged = JSONObjectMerge(target, source, { "$.store.book": "PREPEND" });
expect(merged).toEqual({
store: {
book: [
{
// books from source are prepended to the original array
category: "fiction",
author: "Evelyn Waugh",
title: "Sword of Honour",
isbn: "0-679-43136-5",
price: 12.99
},
{
category: "reference",
author: "Nigel Rees",
title: "Sayings of the Century",
price: 8.95
}
],
bicycle: {
color: "red",
price: 19.95
}
}
});