-3

I have this object

let x = {
      "name": "Ola",
      "dates": [
        {
          "7.01.2020": [1, 2, 3]
        },
        {
          "8.01.2020": [3 ,4]
        }
      ],
      "id": 7
    }

and I need to be able to delete on button click chosen element from array. Ex. after button click, my object looks like this:

   let x = {
          "name": "Ola",
          "dates": [
            {
              "7.01.2020": [1, 2]
            },
           {
              "8.01.2020": [3 ,4]
            }
          ],
          "id": 7
        }

Acrtually I've managed to filter this arr, the problem is when I try to return new x.dates arr. Please, notice that there are objects with different keys.

Any ideas? Thanks!

EDIT - whole func

    try {
      fetch(`http://localhost:3003/users/${userObject.id}`)
        .then(res => res.json())
        .then(user => {
          const map = new Map(
            user.dates.map((entry, i, arr) => {
              const [key] = Object.keys(entry);
              console.log(entry);
              return [key, entry[key]];
            })
          );
          result = map.get(date.toLocaleDateString());
          console.log(user.dates);
          return user;
        })
        .then(user => {
          // Create array without deleted task
          userDatesArr = result.filter(el => {
            if (el !== itemText) {
              return el;
            }
          });
          result = userDatesArr;
          // Update json-server - doesn't work, cause user.dates looks the same
          patchFunc(user.dates);
        });
    } catch (err) {
      console.error(err);
    }
;
Ola Kozioł
  • 95
  • 3
  • 10
  • 2
    Have you searched about `.splice` ? Also, can you show us what you have attempted so far? – Calvin Nunes Jan 08 '20 at 11:56
  • What identifies the element to be removed? Just the `3`? Or also `"7.01.2020"`? – T.J. Crowder Jan 08 '20 at 11:57
  • 1
    In [your previous question](https://stackoverflow.com/q/59625319/157247), I (and others) showed you how to find the object with a given key. Please show us how you've tried to apply that code to the problem above. – T.J. Crowder Jan 08 '20 at 11:58
  • @T.J.Crowder yes, and I've managed to use it to ADD some items to arr. Unfortunatelly I don't know how to use it to delete an item. Above I will paste my code – Ola Kozioł Jan 08 '20 at 12:01
  • Please click edit then `[<>]` snippet editor and produce a [mcve] with relevant input and expected output – mplungjan Jan 08 '20 at 12:06
  • I recommend that you step back from your current task and work through some tutorials on promises, and also on JavaScript variable scope. – T.J. Crowder Jan 08 '20 at 12:21

2 Answers2

2

There are a few issues with that code:

  1. You're assigning to a variable that isn't declared anywhere (result). That means the code is falling prey to what I call The Horror of Implicit Globals. You need to declare your variables. I strongly recommend turning on strict mode so the JavaScript engine will tell you when you do this.

  2. That's now how you use filter, the callback should return a flag saying whether to keep the entry.

  3. You're not checking for HTTP success. This is a footgun in the fetch API I write about here. You need to check ok on the response before calling json.

  4. itemText is a string, but your array contains numbers. No string is ever === a number.

  5. There is no reason for separating the code in your first then handler from the second, there's no intervening promise.

  6. There's no reason to create the Map if you're not going to reuse it. You can just find the object once.

  7. Nothing in the code handles errors the ajax call or the fulfillment handlers. (The try/catch you have wrapped around it will only catch errors calling fetch, not errors in the fetch operation of subsequent then handlers.)

Here's an updated version with some notes:

  fetch(`http://localhost:3003/users/${userObject.id}`)
    .then(res => {
      // *** Check for HTTP success
      if (!res.ok) {
        throw new Error("HTTP error " + res.status);
      }
      return res.json();
    })
    .then(user => {
      const targetValue = +itemText; // *** Convert to number
      for (const entry of user.dates) {
        const targetKey = Object.keys(entry)[0];
        if (targetKey === key) {
          // *** Remove the entry if present
          entry[targetKey] = entry[targetKey].filter(value => value !== targetValue);
          break;
        }
      }
      // Update json-server
      patchFunc(user.dates);
    });

Note that that doesn't flag it up if the object for the target date isn't found. If you want to do that, you can add a flag:

  fetch(`http://localhost:3003/users/${userObject.id}`)
    .then(res => {
      // *** Check for HTTP success
      if (!res.ok) {
        throw new Error("HTTP error " + res.status);
      }
      return res.json();
    })
    .then(user => {
      const targetValue = +itemText; // *** Convert to number
      let found = false;
      for (const entry of user.dates) {
        const targetKey = Object.keys(entry)[0];
        if (targetKey === key) {
          // *** Remove the entry if present
          entry[targetKey] = entry[targetKey].filter(value => value !== targetValue);
          found = true;
          break;
        }
      }
      if (!found) {
        // *** Handle the fact it wasn't found
      }
      // Update json-server
      patchFunc(user.dates);
    });

You'll also want to add a rejection handler (.catch) to handle errors.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I had declared result, but above the function and forgot to copy it over here. Thanks a lot for the explanation and solution - I needed to make some changes and now everything is as it sholud be :) – Ola Kozioł Jan 08 '20 at 12:28
0

I think the below code snippet will give a better understanding of the problem.

Check the console, you will get the desired output. You need to do a deep cloning to avoid mutating the existing object (Deep Cloning - thanks to @nemisj)

let x = {
  "name": "Ola",
  "dates": [{
      "7.01.2020": [1, 2, 3]
    },
    {
      "8.01.2020": [3, 4]
    }
  ],
  "id": 7
}

function clone(item) {
    if (!item) { return item; } // null, undefined values check

    var types = [ Number, String, Boolean ], 
        result;

    // normalizing primitives if someone did new String('aaa'), or new Number('444');
    types.forEach(function(type) {
        if (item instanceof type) {
            result = type( item );
        }
    });

    if (typeof result == "undefined") {
        if (Object.prototype.toString.call( item ) === "[object Array]") {
            result = [];
            item.forEach(function(child, index, array) { 
                result[index] = clone( child );
            });
        } else if (typeof item == "object") {
            // testing that this is DOM
            if (item.nodeType && typeof item.cloneNode == "function") {
                result = item.cloneNode( true );    
            } else if (!item.prototype) { // check that this is a literal
                if (item instanceof Date) {
                    result = new Date(item);
                } else {
                    // it is an object literal
                    result = {};
                    for (var i in item) {
                        result[i] = clone( item[i] );
                    }
                }
            } else {
                // depending what you would like here,
                // just keep the reference, or create new object
                if (false && item.constructor) {
                    // would not advice to do that, reason? Read below
                    result = new item.constructor();
                } else {
                    result = item;
                }
            }
        } else {
            result = item;
        }
    }

    return result;
}


function deleteData (date, elementToBeDel) {
  let newObj = clone(x) // spreading to avoid mutating the object
  newObj.dates.map(dt => {
    if(Object.keys(dt)[0] === date){
      if(dt[date].indexOf(elementToBeDel) > -1){
        dt[date].splice(dt[date].indexOf(elementToBeDel), 1);
      }
    }
  })
  console.log("old Object", x)
  console.log("new Object",newObj)
}
<button onclick="deleteData('7.01.2020', 3)">Click Me to delete</button>
Learner
  • 8,379
  • 7
  • 44
  • 82